forked from akai/readest
feat(settings): unified Cloud Sync chooser with Readest Cloud as a first-class provider (#4976)
The Integrations section is now one Cloud Sync chooser: Readest Cloud,
WebDAV, and Google Drive as radio rows, Readest Cloud first, with a
scope subtitle stating what the choice governs (library data, on this
device) and what always stays with the Readest account (settings,
statistics, dictionaries).
- Activation helpers move to services (cloudSyncActivation.ts) and
accept 'readest' (= no third-party provider active); the component
module re-exports for existing imports.
- Row status lines come from a pure, fully-tested state matrix
(cloudSyncStatus.ts) so every string is enumerated in one place:
signed-out / loading / active / available for Readest Cloud;
not connected / configured / paused / syncing / sync failed /
book-file-uploads-off warning / active for third-party rows. The
paused state renders on the affected third-party row (the plan sketch
placed it on the Readest row; the provider that is paused is the
third-party one).
- The Readest Cloud row opens an inline sub-page (SubPageHeader + the
storage/translation Quota + a NavigationRow out to Account) instead of
ejecting from the Settings dialog; signed-out taps go to login and the
radio is unchecked and disabled, so an idle signed-out state never
shows a checked radio while nothing syncs.
- Premium-gated builds keep the Readest Cloud row and show the upgrade
prompt only in place of the third-party rows.
- Two-direction capability Tips in the WebDAV/Drive sub-pages spell out
both what syncs only to the user's server and what still flows through
the Readest account; the Upload Book Files description notes that
Readest Cloud uploads pause while the provider is selected.
- Manage Sync book/progress/note rows swap their description to
'Managed by {{provider}}...' via the existing locked-row pattern while
a third-party provider is selected; the toggles stay interactive and
persist since they govern the native channels after switch-back.
- The chooser rows form a radiogroup (native same-name radios provide
arrow-key group movement) with an accessible group label.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,69 +1,10 @@
|
||||
import type { SystemSettings } from '@/types/settings';
|
||||
import type { EnvConfigType } from '@/services/environment';
|
||||
import type { FileSyncBackendKind } from '@/services/sync/file/providerRegistry';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { broadcastGlobalSettings } from '@/utils/settingsSync';
|
||||
|
||||
/**
|
||||
* Return settings with exactly one third-party cloud-sync provider active (or
|
||||
* none). WebDAV and Google Drive are mutually exclusive — only one syncs the
|
||||
* library at a time — so enabling one always disables the other. Provider config
|
||||
* (WebDAV creds, the Drive keychain token) is left untouched, so switching back
|
||||
* to a previously-configured provider needs no re-entry; only an explicit
|
||||
* Disconnect tears a provider's config down.
|
||||
*
|
||||
* Activating a provider (disabled -> enabled) also turns its `syncBooks` on:
|
||||
* the selected provider owns the book-file channel — native Readest Cloud
|
||||
* uploads gate off — so leaving syncBooks at its `false` default would back
|
||||
* books up nowhere. An explicit opt-out while the provider stays active is
|
||||
* respected (re-activation of an already-active provider changes nothing).
|
||||
* Re-export shim: the activation helpers moved to the services layer
|
||||
* (`src/services/sync/cloudSyncActivation.ts`) so service modules never
|
||||
* import from components. Existing component-side imports keep working.
|
||||
*/
|
||||
export const withActiveCloudProvider = (
|
||||
settings: SystemSettings,
|
||||
active: FileSyncBackendKind | null,
|
||||
): SystemSettings => ({
|
||||
...settings,
|
||||
webdav: {
|
||||
...settings.webdav,
|
||||
enabled: active === 'webdav',
|
||||
...(active === 'webdav' && !settings.webdav?.enabled
|
||||
? { syncBooks: true, providerSelectedAt: Date.now() }
|
||||
: {}),
|
||||
},
|
||||
googleDrive: {
|
||||
...settings.googleDrive,
|
||||
enabled: active === 'gdrive',
|
||||
...(active === 'gdrive' && !settings.googleDrive?.enabled
|
||||
? { syncBooks: true, providerSelectedAt: Date.now() }
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* The single write path for changing the selected cloud sync provider.
|
||||
* Every activation/deactivation surface (the Integrations chooser, the
|
||||
* WebDAV/Drive connect and disconnect flows, the Drive OAuth callback)
|
||||
* routes through here so the change always (a) persists, (b) hydrates the
|
||||
* settings store even on routes where it wasn't loaded yet (the OAuth
|
||||
* callback), and (c) broadcasts the provider flags to other windows —
|
||||
* a stale reader window would otherwise clobber the switch on its next
|
||||
* whole-file save, silently resuming Readest Cloud uploads.
|
||||
*
|
||||
* `mutate` runs BEFORE activation so connect flows can apply credentials
|
||||
* or account labels without pre-setting `enabled` (which would suppress
|
||||
* the activation side effects).
|
||||
*/
|
||||
export const persistActiveCloudProvider = async (
|
||||
envConfig: EnvConfigType,
|
||||
active: FileSyncBackendKind | null,
|
||||
mutate: (settings: SystemSettings) => SystemSettings = (s) => s,
|
||||
): Promise<SystemSettings> => {
|
||||
const store = useSettingsStore.getState();
|
||||
const appService = await envConfig.getAppService();
|
||||
const current = store.settings?.version ? store.settings : await appService.loadSettings();
|
||||
const next = withActiveCloudProvider(mutate(current), active);
|
||||
store.setSettings(next);
|
||||
await appService.saveSettings(next);
|
||||
void broadcastGlobalSettings(next, { includeCloudSyncProviders: true });
|
||||
return next;
|
||||
};
|
||||
export {
|
||||
withActiveCloudProvider,
|
||||
persistActiveCloudProvider,
|
||||
type CloudSyncActivationKind,
|
||||
} from '@/services/sync/cloudSyncActivation';
|
||||
|
||||
Reference in New Issue
Block a user