@@ -3,7 +3,10 @@ import { LoadingOutlined } from '@ant-design/icons';
33import { useDispatch , useSelector } from 'react-redux' ;
44import { Button , Spin } from 'antd' ;
55import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' ;
6- import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons' ;
6+ import {
7+ faExclamationTriangle ,
8+ faFileDownload
9+ } from '@fortawesome/free-solid-svg-icons' ;
710import { ipcRenderer } from 'electron' ;
811import styled from 'styled-components' ;
912import Modal from '../components/Modal' ;
@@ -56,7 +59,14 @@ const RowContainer = styled.div`
5659 }
5760` ;
5861
59- const ModRow = ( { mod, loadedMods, currentMod, missingMods } ) => {
62+ const ModRow = ( {
63+ mod,
64+ loadedMods,
65+ currentMod,
66+ missingMods,
67+ cloudflareBlock,
68+ downloadUrl
69+ } ) => {
6070 const { modManifest, addon } = mod ;
6171 const loaded = loadedMods . includes ( modManifest . id ) ;
6272 const missing = missingMods . includes ( modManifest . id ) ;
@@ -77,15 +87,20 @@ const ModRow = ({ mod, loadedMods, currentMod, missingMods }) => {
7787 return (
7888 < RowContainer ref = { ref } >
7989 < div > { `${ addon ?. name } - ${ modManifest ?. displayName } ` } </ div >
80- { loaded && ! missing && < div className = "dot" /> }
81- { loaded && missing && (
90+ { loaded && ! missing && ! cloudflareBlock && < div className = "dot" /> }
91+ { loaded && missing && ! cloudflareBlock && (
8292 < FontAwesomeIcon
8393 icon = { faExclamationTriangle }
8494 css = { `
8595 color: ${ props => props . theme . palette . colors . yellow } ;
8696 ` }
8797 />
8898 ) }
99+ { loaded && ! missing && cloudflareBlock && (
100+ < Button href = { downloadUrl } >
101+ < FontAwesomeIcon icon = { faFileDownload } />
102+ </ Button >
103+ ) }
89104 { ! loaded && isCurrentMod && (
90105 < Spin indicator = { < LoadingOutlined style = { { fontSize : 24 } } spin /> } />
91106 ) }
@@ -102,6 +117,8 @@ const OptedOutModsList = ({
102117} ) => {
103118 const [ loadedMods , setLoadedMods ] = useState ( [ ] ) ;
104119 const [ missingMods , setMissingMods ] = useState ( [ ] ) ;
120+ const [ cloudflareBlock , setCloudflareBlock ] = useState ( false ) ;
121+ const [ manualDownloadUrls , setManualDownloadUrls ] = useState ( [ ] ) ;
105122 const [ downloading , setDownloading ] = useState ( false ) ;
106123
107124 const dispatch = useDispatch ( ) ;
@@ -135,14 +152,21 @@ const OptedOutModsList = ({
135152 const listener = ( e , status ) => {
136153 if ( ! status . error ) {
137154 if ( optedOutMods . length === loadedMods . length + 1 ) {
138- if ( missingMods . length === 0 ) {
155+ if ( missingMods . length === 0 && ! cloudflareBlock ) {
139156 resolve ( ) ;
140157 dispatch ( closeModal ( ) ) ;
141158 }
142159 setDownloading ( false ) ;
143160 }
144161 setLoadedMods ( prev => [ ...prev , status . modId ] ) ;
145- if ( status . warning ) setMissingMods ( prev => [ ...prev , status . modId ] ) ;
162+ if ( status . warning ) {
163+ if ( ! status . cloudflareBlock ) {
164+ setMissingMods ( prev => [ ...prev , status . modId ] ) ;
165+ } else {
166+ setCloudflareBlock ( true ) ;
167+ setManualDownloadUrls ( prev => [ ...prev , status . modId ] ) ;
168+ }
169+ }
146170 } else {
147171 dispatch ( closeModal ( ) ) ;
148172 setTimeout ( ( ) => {
@@ -159,7 +183,7 @@ const OptedOutModsList = ({
159183 listener
160184 ) ;
161185 } ;
162- } , [ loadedMods , missingMods ] ) ;
186+ } , [ loadedMods , missingMods , cloudflareBlock , manualDownloadUrls ] ) ;
163187
164188 return (
165189 < Modal
@@ -191,15 +215,32 @@ const OptedOutModsList = ({
191215 </ div >
192216 < ModsContainer >
193217 { optedOutMods &&
194- optedOutMods . map ( mod => (
195- < ModRow
196- mod = { mod }
197- loadedMods = { loadedMods }
198- currentMod = { currentMod }
199- missingMods = { missingMods }
200- />
201- ) ) }
218+ optedOutMods . map ( mod => {
219+ return (
220+ < ModRow
221+ mod = { mod }
222+ loadedMods = { loadedMods }
223+ currentMod = { currentMod }
224+ missingMods = { missingMods }
225+ cloudflareBlock = { cloudflareBlock }
226+ downloadUrl = { `${ mod . addon . links . websiteUrl } /download/${ mod . modManifest . id } ` }
227+ />
228+ ) ;
229+ } ) }
202230 </ ModsContainer >
231+ { cloudflareBlock && (
232+ < p
233+ css = { `
234+ width: 90%;
235+ margin: 20px auto 0 auto;
236+ ` }
237+ >
238+ Cloudflare is currently blocking automated downloads. You can
239+ manually download the mods and place them in the mods folder to
240+ continue. Use the download buttons in the rows above, and the button
241+ below to open the instance folder.
242+ </ p >
243+ ) }
203244 < div
204245 css = { `
205246 display: flex;
@@ -224,7 +265,7 @@ const OptedOutModsList = ({
224265 >
225266 Cancel
226267 </ Button >
227- { missingMods . length === 0 && (
268+ { missingMods . length === 0 && ! cloudflareBlock && (
228269 < Button
229270 type = "primary"
230271 disabled = { downloading }
@@ -257,7 +298,7 @@ const OptedOutModsList = ({
257298 Confirm
258299 </ Button >
259300 ) }
260- { missingMods . length > 0 && (
301+ { missingMods . length > 0 && ! cloudflareBlock && (
261302 < Button
262303 type = "primary"
263304 disabled = { downloading }
@@ -272,6 +313,36 @@ const OptedOutModsList = ({
272313 Continue
273314 </ Button >
274315 ) }
316+ { cloudflareBlock && (
317+ < >
318+ < Button
319+ type = "primary"
320+ disabled = { downloading }
321+ onClick = { ( ) => {
322+ ipcRenderer . invoke ( 'openFolder' , instancePath ) ;
323+ } }
324+ css = { `
325+ background-color: ${ props => props . theme . palette . colors . blue } ;
326+ ` }
327+ >
328+ Open folder
329+ </ Button >
330+ < Button
331+ type = "primary"
332+ disabled = { downloading }
333+ onClick = { ( ) => {
334+ resolve ( ) ;
335+ dispatch ( closeModal ( ) ) ;
336+ } }
337+ css = { `
338+ background-color: ${ props =>
339+ props . theme . palette . colors . green } ;
340+ ` }
341+ >
342+ Continue
343+ </ Button >
344+ </ >
345+ ) }
275346 </ div >
276347 </ Container >
277348 </ Modal >
0 commit comments