import { createSlice, PayloadAction, createAsyncThunk } from '@reduxjs/toolkit'; import { createFile, deleteFile, getPersistedUriPermissions, } from 'react-native-scoped-storage'; import { FileSystem, AndroidScoped } from 'react-native-file-access'; import { clearPermissions, isPermissionForPath } from '../utilities'; import { RootState } from '.'; interface SettingsState { storageUri: string | undefined; noMedia: boolean; } const initialState: SettingsState = { storageUri: undefined, noMedia: false, }; const settingsSlice = createSlice({ name: 'settings', initialState, reducers: { setStorageUri: (state, action: PayloadAction) => { state.storageUri = action.payload; }, setNoMedia: (state, action: PayloadAction) => { state.noMedia = action.payload; }, }, }); const { setStorageUri, setNoMedia } = settingsSlice.actions; const updateStorageUri = createAsyncThunk( 'settings/updateStorageUri', async (newStorageUri: string, { dispatch }) => { await clearPermissions([newStorageUri]); dispatch(setStorageUri(newStorageUri)); }, ); const updateNoMedia = createAsyncThunk( 'settings/updateNoMedia', async (newNoMedia: boolean, { dispatch, getState }) => { dispatch(setNoMedia(newNoMedia)); const state = getState() as RootState; const { storageUri } = state.settings; if (!storageUri) return; const noMediaExists = await FileSystem.exists( AndroidScoped.appendPath(storageUri, '.nomedia'), ); if (newNoMedia && !noMediaExists) { await createFile(storageUri, '.nomedia', 'text/x-unknown'); } else if (!newNoMedia && noMediaExists) { await deleteFile(AndroidScoped.appendPath(storageUri, '.nomedia')); } }, ); const validateSettings = createAsyncThunk( 'settings/validateSettings', // eslint-disable-next-line @typescript-eslint/naming-convention async (_, { dispatch, getState }) => { const state = getState() as RootState; const { storageUri, noMedia } = state.settings; if (!storageUri) { return; } const permissions = await getPersistedUriPermissions(); if ( !permissions.some(permission => isPermissionForPath(permission, storageUri), ) ) { dispatch(setStorageUri()); return; } try { const exists = await FileSystem.exists(storageUri); if (!exists) { throw new Error('Storage URI does not exist'); } const isDirectory = await FileSystem.isDir(storageUri); if (!isDirectory) { throw new Error('Storage URI is not a directory'); } } catch { dispatch(setStorageUri()); return; } const isNoMedia = await FileSystem.exists( AndroidScoped.appendPath(storageUri, '.nomedia'), ); if (noMedia !== isNoMedia) { dispatch(setNoMedia(isNoMedia)); } }, ); export { type SettingsState, updateStorageUri as setStorageUri, updateNoMedia as setNoMedia, validateSettings, }; export default settingsSlice.reducer;