diff --git a/app/soapbox/actions/sw.ts b/app/soapbox/actions/sw.ts new file mode 100644 index 000000000..c12dc83e8 --- /dev/null +++ b/app/soapbox/actions/sw.ts @@ -0,0 +1,15 @@ +import type { AnyAction } from 'redux'; + +/** Sets the ServiceWorker updating state. */ +const SW_UPDATING = 'SW_UPDATING'; + +/** Dispatch when the ServiceWorker is being updated to display a loading screen. */ +const setSwUpdating = (isUpdating: boolean): AnyAction => ({ + type: SW_UPDATING, + isUpdating, +}); + +export { + SW_UPDATING, + setSwUpdating, +}; diff --git a/app/soapbox/containers/soapbox.tsx b/app/soapbox/containers/soapbox.tsx index 22c832233..e87d858d1 100644 --- a/app/soapbox/containers/soapbox.tsx +++ b/app/soapbox/containers/soapbox.tsx @@ -78,6 +78,7 @@ const SoapboxMount = () => { const settings = useSettings(); const soapboxConfig = useSoapboxConfig(); const features = useFeatures(); + const swUpdating = useAppSelector(state => state.meta.swUpdating); const locale = validLocale(settings.get('locale')) ? settings.get('locale') : 'en'; @@ -120,6 +121,7 @@ const SoapboxMount = () => { me && !account, !isLoaded, localeLoading, + swUpdating, ].some(Boolean); const bodyClass = classNames('bg-white dark:bg-slate-900 text-base h-full', { diff --git a/app/soapbox/main.tsx b/app/soapbox/main.tsx index 9456d9aa3..84ea3739a 100644 --- a/app/soapbox/main.tsx +++ b/app/soapbox/main.tsx @@ -7,6 +7,7 @@ import ReactDOM from 'react-dom'; import { defineMessages } from 'react-intl'; import snackbar from 'soapbox/actions/snackbar'; +import { setSwUpdating } from 'soapbox/actions/sw'; import * as BuildConfig from 'soapbox/build_config'; import { store } from 'soapbox/store'; import { printConsoleWarning } from 'soapbox/utils/console'; @@ -45,6 +46,7 @@ function main() { store.dispatch(snackbar.show('info', messages.updateText, { actionLabel: messages.update, action: () => { + store.dispatch(setSwUpdating(true)); OfflinePluginRuntime.applyUpdate(); }, dismissAfter: false, diff --git a/app/soapbox/reducers/__tests__/meta.test.ts b/app/soapbox/reducers/__tests__/meta.test.ts index 92ee4e6ff..d318b4a30 100644 --- a/app/soapbox/reducers/__tests__/meta.test.ts +++ b/app/soapbox/reducers/__tests__/meta.test.ts @@ -1,5 +1,7 @@ import { Record as ImmutableRecord } from 'immutable'; +import { SW_UPDATING, setSwUpdating } from 'soapbox/actions/sw'; + import reducer from '../meta'; describe('meta reducer', () => { @@ -7,5 +9,13 @@ describe('meta reducer', () => { const result = reducer(undefined, {}); expect(ImmutableRecord.isRecord(result)).toBe(true); expect(result.instance_fetch_failed).toBe(false); + expect(result.swUpdating).toBe(false); + }); + + describe(SW_UPDATING, () => { + it('sets swUpdating to the provided value', () => { + const result = reducer(undefined, setSwUpdating(true)); + expect(result.swUpdating).toBe(true); + }); }); }); diff --git a/app/soapbox/reducers/meta.ts b/app/soapbox/reducers/meta.ts index f793372c8..fdce05e3e 100644 --- a/app/soapbox/reducers/meta.ts +++ b/app/soapbox/reducers/meta.ts @@ -3,11 +3,15 @@ import { Record as ImmutableRecord } from 'immutable'; import { fetchInstance } from 'soapbox/actions/instance'; +import { SW_UPDATING } from 'soapbox/actions/sw'; import type { AnyAction } from 'redux'; const ReducerRecord = ImmutableRecord({ + /** Whether /api/v1/instance 404'd (and we should display the external auth form). */ instance_fetch_failed: false, + /** Whether the ServiceWorker is currently updating (and we should display a loading screen). */ + swUpdating: false, }); export default function meta(state = ReducerRecord(), action: AnyAction) { @@ -17,6 +21,8 @@ export default function meta(state = ReducerRecord(), action: AnyAction) { return state.set('instance_fetch_failed', true); } return state; + case SW_UPDATING: + return state.set('swUpdating', action.isUpdating); default: return state; }