mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-07 01:09:54 +08:00
fix(electron): sync settings from localStorage -> atom -> electron (#5020)
- moved `appSettingAtom` to infra since we now have different packages that depends on it. There is no better place to fit in for now - use atomEffect to sync setting changes to updater related configs to Electron side - refactored how Electron reacts to updater config changes.
This commit is contained in:
Vendored
+1
-2
@@ -21,8 +21,7 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@affine/templates": "workspace:*",
|
"@affine/templates": "workspace:*",
|
||||||
"@blocksuite/global": "0.0.0-20230409084303-221991d4-nightly",
|
"@blocksuite/global": "0.0.0-20230409084303-221991d4-nightly"
|
||||||
"@toeverything/infra": "workspace:*"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lit": "^3.0.2"
|
"lit": "^3.0.2"
|
||||||
|
|||||||
Vendored
-3
@@ -7,9 +7,6 @@
|
|||||||
"outDir": "lib"
|
"outDir": "lib"
|
||||||
},
|
},
|
||||||
"references": [
|
"references": [
|
||||||
{
|
|
||||||
"path": "../infra"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "../../../tests/fixtures"
|
"path": "../../../tests/fixtures"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@
|
|||||||
"dev": "vite build --watch"
|
"dev": "vite build --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@affine/env": "workspace:*",
|
||||||
"@affine/sdk": "workspace:*",
|
"@affine/sdk": "workspace:*",
|
||||||
"@blocksuite/blocks": "0.11.0-nightly-202312070955-2b5bb47",
|
"@blocksuite/blocks": "0.11.0-nightly-202312070955-2b5bb47",
|
||||||
"@blocksuite/global": "0.11.0-nightly-202312070955-2b5bb47",
|
"@blocksuite/global": "0.11.0-nightly-202312070955-2b5bb47",
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { atom } from 'jotai';
|
||||||
|
|
||||||
|
export const loadedPluginNameAtom = atom<string[]>([]);
|
||||||
|
|
||||||
|
export * from './layout';
|
||||||
|
export * from './root-store';
|
||||||
|
export * from './settings';
|
||||||
|
export * from './workspace';
|
||||||
@@ -1,34 +1,5 @@
|
|||||||
import type { ExpectedLayout } from '@affine/sdk/entry';
|
import type { ExpectedLayout } from '@affine/sdk/entry';
|
||||||
import { assertExists } from '@blocksuite/global/utils';
|
import { atom } from 'jotai';
|
||||||
import type { Workspace } from '@blocksuite/store';
|
|
||||||
import { atom, createStore } from 'jotai/vanilla';
|
|
||||||
|
|
||||||
import { getBlockSuiteWorkspaceAtom } from './__internal__/workspace';
|
|
||||||
|
|
||||||
// global store
|
|
||||||
let rootStore = createStore();
|
|
||||||
|
|
||||||
export function getCurrentStore() {
|
|
||||||
return rootStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal do not use this function unless you know what you are doing
|
|
||||||
*/
|
|
||||||
export function _setCurrentStore(store: ReturnType<typeof createStore>) {
|
|
||||||
rootStore = store;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const loadedPluginNameAtom = atom<string[]>([]);
|
|
||||||
|
|
||||||
export const currentWorkspaceIdAtom = atom<string | null>(null);
|
|
||||||
export const currentPageIdAtom = atom<string | null>(null);
|
|
||||||
export const currentWorkspaceAtom = atom<Promise<Workspace>>(async get => {
|
|
||||||
const workspaceId = get(currentWorkspaceIdAtom);
|
|
||||||
assertExists(workspaceId);
|
|
||||||
const [currentWorkspaceAtom] = getBlockSuiteWorkspaceAtom(workspaceId);
|
|
||||||
return get(currentWorkspaceAtom);
|
|
||||||
});
|
|
||||||
|
|
||||||
const contentLayoutBaseAtom = atom<ExpectedLayout>('editor');
|
const contentLayoutBaseAtom = atom<ExpectedLayout>('editor');
|
||||||
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { createStore } from 'jotai';
|
||||||
|
|
||||||
|
// global store
|
||||||
|
let rootStore = createStore();
|
||||||
|
|
||||||
|
export function getCurrentStore() {
|
||||||
|
return rootStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal do not use this function unless you know what you are doing
|
||||||
|
*/
|
||||||
|
export function _setCurrentStore(store: ReturnType<typeof createStore>) {
|
||||||
|
rootStore = store;
|
||||||
|
}
|
||||||
+29
-5
@@ -1,5 +1,9 @@
|
|||||||
|
import { setupGlobal } from '@affine/env/global';
|
||||||
import { atom } from 'jotai';
|
import { atom } from 'jotai';
|
||||||
import { atomWithStorage } from 'jotai/utils';
|
import { atomWithStorage } from 'jotai/utils';
|
||||||
|
import { atomEffect } from 'jotai-effect';
|
||||||
|
|
||||||
|
setupGlobal();
|
||||||
|
|
||||||
export type DateFormats =
|
export type DateFormats =
|
||||||
| 'MM/dd/YYYY'
|
| 'MM/dd/YYYY'
|
||||||
@@ -63,15 +67,35 @@ const appSettingBaseAtom = atomWithStorage<AppSetting>('affine-settings', {
|
|||||||
|
|
||||||
type SetStateAction<Value> = Value | ((prev: Value) => Value);
|
type SetStateAction<Value> = Value | ((prev: Value) => Value);
|
||||||
|
|
||||||
|
const appSettingEffect = atomEffect(get => {
|
||||||
|
const settings = get(appSettingBaseAtom);
|
||||||
|
// some values in settings should be synced into electron side
|
||||||
|
if (environment.isDesktop) {
|
||||||
|
console.log('set config', settings);
|
||||||
|
window.apis?.updater
|
||||||
|
.setConfig({
|
||||||
|
autoCheckUpdate: settings.autoCheckUpdate,
|
||||||
|
autoDownloadUpdate: settings.autoDownloadUpdate,
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export const appSettingAtom = atom<
|
export const appSettingAtom = atom<
|
||||||
AppSetting,
|
AppSetting,
|
||||||
[SetStateAction<Partial<AppSetting>>],
|
[SetStateAction<Partial<AppSetting>>],
|
||||||
void
|
void
|
||||||
>(
|
>(
|
||||||
get => get(appSettingBaseAtom),
|
get => {
|
||||||
(get, set, apply) => {
|
get(appSettingEffect);
|
||||||
const prev = get(appSettingBaseAtom);
|
return get(appSettingBaseAtom);
|
||||||
const next = typeof apply === 'function' ? apply(prev) : apply;
|
},
|
||||||
set(appSettingBaseAtom, { ...prev, ...next });
|
(_get, set, apply) => {
|
||||||
|
set(appSettingBaseAtom, prev => {
|
||||||
|
const next = typeof apply === 'function' ? apply(prev) : apply;
|
||||||
|
return { ...prev, ...next };
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { assertExists } from '@blocksuite/global/utils';
|
||||||
|
import type { Workspace } from '@blocksuite/store';
|
||||||
|
import { atom } from 'jotai';
|
||||||
|
|
||||||
|
import { getBlockSuiteWorkspaceAtom } from '../__internal__/workspace';
|
||||||
|
|
||||||
|
export const currentWorkspaceIdAtom = atom<string | null>(null);
|
||||||
|
export const currentPageIdAtom = atom<string | null>(null);
|
||||||
|
export const currentWorkspaceAtom = atom<Promise<Workspace>>(async get => {
|
||||||
|
const workspaceId = get(currentWorkspaceIdAtom);
|
||||||
|
assertExists(workspaceId);
|
||||||
|
const [currentWorkspaceAtom] = getBlockSuiteWorkspaceAtom(workspaceId);
|
||||||
|
return get(currentWorkspaceAtom);
|
||||||
|
});
|
||||||
@@ -201,7 +201,7 @@ export type UpdaterHandlers = {
|
|||||||
downloadUpdate: () => Promise<void>;
|
downloadUpdate: () => Promise<void>;
|
||||||
getConfig: () => Promise<UpdaterConfig>;
|
getConfig: () => Promise<UpdaterConfig>;
|
||||||
setConfig: (newConfig: Partial<UpdaterConfig>) => Promise<void>;
|
setConfig: (newConfig: Partial<UpdaterConfig>) => Promise<void>;
|
||||||
checkForUpdatesAndNotify: () => Promise<{ version: string } | null>;
|
checkForUpdates: () => Promise<{ version: string } | null>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WorkspaceHandlers = {
|
export type WorkspaceHandlers = {
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
{
|
{
|
||||||
"path": "../sdk"
|
"path": "../sdk"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "../env"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "./tsconfig.node.json"
|
"path": "./tsconfig.node.json"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export default defineConfig({
|
|||||||
entry: {
|
entry: {
|
||||||
blocksuite: resolve(root, 'src/blocksuite/index.ts'),
|
blocksuite: resolve(root, 'src/blocksuite/index.ts'),
|
||||||
index: resolve(root, 'src/index.ts'),
|
index: resolve(root, 'src/index.ts'),
|
||||||
atom: resolve(root, 'src/atom.ts'),
|
atom: resolve(root, 'src/atom/index.ts'),
|
||||||
command: resolve(root, 'src/command/index.ts'),
|
command: resolve(root, 'src/command/index.ts'),
|
||||||
type: resolve(root, 'src/type.ts'),
|
type: resolve(root, 'src/type.ts'),
|
||||||
'core/event-emitter': resolve(root, 'src/core/event-emitter.ts'),
|
'core/event-emitter': resolve(root, 'src/core/event-emitter.ts'),
|
||||||
|
|||||||
+2
-1
@@ -85,12 +85,12 @@ export const installLabel = style({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
fontSize: 'var(--affine-font-sm)',
|
fontSize: 'var(--affine-font-sm)',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
|
justifyContent: 'space-between',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const installLabelNormal = style([
|
export const installLabelNormal = style([
|
||||||
installLabel,
|
installLabel,
|
||||||
{
|
{
|
||||||
justifyContent: 'space-between',
|
|
||||||
selectors: {
|
selectors: {
|
||||||
[`${root}:hover &, ${root}[data-updating=true] &`]: {
|
[`${root}:hover &, ${root}[data-updating=true] &`]: {
|
||||||
display: 'none',
|
display: 'none',
|
||||||
@@ -103,6 +103,7 @@ export const installLabelHover = style([
|
|||||||
installLabel,
|
installLabel,
|
||||||
{
|
{
|
||||||
display: 'none',
|
display: 'none',
|
||||||
|
justifyContent: 'flex-start',
|
||||||
selectors: {
|
selectors: {
|
||||||
[`${root}:hover &, ${root}[data-updating=true] &`]: {
|
[`${root}:hover &, ${root}[data-updating=true] &`]: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
|||||||
+206
-135
@@ -1,18 +1,9 @@
|
|||||||
import { Unreachable } from '@affine/env/constant';
|
import { Unreachable } from '@affine/env/constant';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { CloseIcon, NewIcon, ResetIcon } from '@blocksuite/icons';
|
import { CloseIcon, NewIcon, ResetIcon } from '@blocksuite/icons';
|
||||||
import {
|
import { useAppUpdater } from '@toeverything/hooks/use-app-updater';
|
||||||
changelogCheckedAtom,
|
|
||||||
currentChangelogUnreadAtom,
|
|
||||||
currentVersionAtom,
|
|
||||||
downloadProgressAtom,
|
|
||||||
updateAvailableAtom,
|
|
||||||
updateReadyAtom,
|
|
||||||
useAppUpdater,
|
|
||||||
} from '@toeverything/hooks/use-app-updater';
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useAtomValue, useSetAtom } from 'jotai';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { startTransition, useCallback } from 'react';
|
|
||||||
|
|
||||||
import { Tooltip } from '../../../ui/tooltip';
|
import { Tooltip } from '../../../ui/tooltip';
|
||||||
import * as styles from './index.css';
|
import * as styles from './index.css';
|
||||||
@@ -26,36 +17,178 @@ export interface AddPageButtonPureProps {
|
|||||||
version: string;
|
version: string;
|
||||||
allowAutoUpdate: boolean;
|
allowAutoUpdate: boolean;
|
||||||
} | null;
|
} | null;
|
||||||
|
autoDownload: boolean;
|
||||||
downloadProgress: number | null;
|
downloadProgress: number | null;
|
||||||
appQuitting: boolean;
|
appQuitting: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ButtonContentProps {
|
||||||
|
updateReady: boolean;
|
||||||
|
updateAvailable: {
|
||||||
|
version: string;
|
||||||
|
allowAutoUpdate: boolean;
|
||||||
|
} | null;
|
||||||
|
autoDownload: boolean;
|
||||||
|
downloadProgress: number | null;
|
||||||
|
appQuitting: boolean;
|
||||||
|
currentChangelogUnread: boolean;
|
||||||
|
onDismissCurrentChangelog: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DownloadUpdate({ updateAvailable }: ButtonContentProps) {
|
||||||
|
const t = useAFFiNEI18N();
|
||||||
|
return (
|
||||||
|
<div className={styles.installLabel}>
|
||||||
|
<span className={styles.ellipsisTextOverflow}>
|
||||||
|
{t['com.affine.appUpdater.downloadUpdate']()}
|
||||||
|
</span>
|
||||||
|
<span className={styles.versionLabel}>{updateAvailable?.version}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function UpdateReady({ updateAvailable, appQuitting }: ButtonContentProps) {
|
||||||
|
const t = useAFFiNEI18N();
|
||||||
|
return (
|
||||||
|
<div className={styles.updateAvailableWrapper}>
|
||||||
|
<div className={styles.installLabelNormal}>
|
||||||
|
<span className={styles.ellipsisTextOverflow}>
|
||||||
|
{t['com.affine.appUpdater.updateAvailable']()}
|
||||||
|
</span>
|
||||||
|
<span className={styles.versionLabel}>{updateAvailable?.version}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.installLabelHover}>
|
||||||
|
<ResetIcon className={styles.icon} />
|
||||||
|
<span className={styles.ellipsisTextOverflow}>
|
||||||
|
{t[appQuitting ? 'Loading' : 'com.affine.appUpdater.installUpdate']()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DownloadingUpdate({
|
||||||
|
updateAvailable,
|
||||||
|
downloadProgress,
|
||||||
|
}: ButtonContentProps) {
|
||||||
|
const t = useAFFiNEI18N();
|
||||||
|
return (
|
||||||
|
<div className={clsx([styles.updateAvailableWrapper])}>
|
||||||
|
<div className={clsx([styles.installLabelNormal])}>
|
||||||
|
<span className={styles.ellipsisTextOverflow}>
|
||||||
|
{t['com.affine.appUpdater.downloading']()}
|
||||||
|
</span>
|
||||||
|
<span className={styles.versionLabel}>{updateAvailable?.version}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.progress}>
|
||||||
|
<div
|
||||||
|
className={styles.progressInner}
|
||||||
|
style={{ width: `${downloadProgress}%` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function OpenDownloadPage({ updateAvailable }: ButtonContentProps) {
|
||||||
|
const t = useAFFiNEI18N();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={styles.installLabelNormal}>
|
||||||
|
<span className={styles.ellipsisTextOverflow}>
|
||||||
|
{t['com.affine.appUpdater.updateAvailable']()}
|
||||||
|
</span>
|
||||||
|
<span className={styles.versionLabel}>{updateAvailable?.version}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.installLabelHover}>
|
||||||
|
<span className={styles.ellipsisTextOverflow}>
|
||||||
|
{t['com.affine.appUpdater.openDownloadPage']()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function WhatsNew({ onDismissCurrentChangelog }: ButtonContentProps) {
|
||||||
|
const t = useAFFiNEI18N();
|
||||||
|
const onClickClose: React.MouseEventHandler = useCallback(
|
||||||
|
e => {
|
||||||
|
onDismissCurrentChangelog();
|
||||||
|
e.stopPropagation();
|
||||||
|
},
|
||||||
|
[onDismissCurrentChangelog]
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={clsx([styles.whatsNewLabel])}>
|
||||||
|
<NewIcon className={styles.icon} />
|
||||||
|
<span className={styles.ellipsisTextOverflow}>
|
||||||
|
{t['com.affine.appUpdater.whatsNew']()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.closeIcon} onClick={onClickClose}>
|
||||||
|
<CloseIcon />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getButtonContentRenderer = (props: ButtonContentProps) => {
|
||||||
|
if (props.updateReady) {
|
||||||
|
return UpdateReady;
|
||||||
|
} else if (props.updateAvailable?.allowAutoUpdate) {
|
||||||
|
if (props.autoDownload && props.updateAvailable.allowAutoUpdate) {
|
||||||
|
return DownloadingUpdate;
|
||||||
|
} else {
|
||||||
|
return DownloadUpdate;
|
||||||
|
}
|
||||||
|
} else if (props.updateAvailable && !props.updateAvailable?.allowAutoUpdate) {
|
||||||
|
return OpenDownloadPage;
|
||||||
|
} else if (props.currentChangelogUnread) {
|
||||||
|
return WhatsNew;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
export function AppUpdaterButtonPure({
|
export function AppUpdaterButtonPure({
|
||||||
updateReady,
|
updateReady,
|
||||||
onClickUpdate,
|
onClickUpdate,
|
||||||
onDismissCurrentChangelog,
|
onDismissCurrentChangelog,
|
||||||
currentChangelogUnread,
|
currentChangelogUnread,
|
||||||
updateAvailable,
|
updateAvailable,
|
||||||
|
autoDownload,
|
||||||
downloadProgress,
|
downloadProgress,
|
||||||
appQuitting,
|
appQuitting,
|
||||||
className,
|
className,
|
||||||
style,
|
style,
|
||||||
}: AddPageButtonPureProps) {
|
}: AddPageButtonPureProps) {
|
||||||
const t = useAFFiNEI18N();
|
const contentProps = useMemo(
|
||||||
|
() => ({
|
||||||
|
updateReady,
|
||||||
|
updateAvailable,
|
||||||
|
currentChangelogUnread,
|
||||||
|
autoDownload,
|
||||||
|
downloadProgress,
|
||||||
|
appQuitting,
|
||||||
|
onDismissCurrentChangelog,
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
updateReady,
|
||||||
|
updateAvailable,
|
||||||
|
currentChangelogUnread,
|
||||||
|
autoDownload,
|
||||||
|
downloadProgress,
|
||||||
|
appQuitting,
|
||||||
|
onDismissCurrentChangelog,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
if (!updateAvailable && !currentChangelogUnread) {
|
const ContentComponent = getButtonContentRenderer(contentProps);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateAvailableNode = updateAvailable
|
|
||||||
? updateAvailable.allowAutoUpdate
|
|
||||||
? renderUpdateAvailableAllowAutoUpdate()
|
|
||||||
: renderUpdateAvailableNotAllowAutoUpdate()
|
|
||||||
: null;
|
|
||||||
const whatsNew =
|
|
||||||
!updateAvailable && currentChangelogUnread ? renderWhatsNew() : null;
|
|
||||||
|
|
||||||
const wrapWithTooltip = (
|
const wrapWithTooltip = (
|
||||||
node: React.ReactElement,
|
node: React.ReactElement,
|
||||||
@@ -72,102 +205,38 @@ export function AppUpdaterButtonPure({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const disabled = useMemo(() => {
|
||||||
|
if (appQuitting) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateAvailable?.allowAutoUpdate) {
|
||||||
|
return !updateReady && autoDownload;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}, [
|
||||||
|
appQuitting,
|
||||||
|
autoDownload,
|
||||||
|
updateAvailable?.allowAutoUpdate,
|
||||||
|
updateReady,
|
||||||
|
]);
|
||||||
|
|
||||||
return wrapWithTooltip(
|
return wrapWithTooltip(
|
||||||
<button
|
<button
|
||||||
style={style}
|
style={style}
|
||||||
className={clsx([styles.root, className])}
|
className={clsx([styles.root, className])}
|
||||||
data-has-update={!!updateAvailable}
|
data-has-update={!!updateAvailable}
|
||||||
data-updating={appQuitting}
|
data-updating={appQuitting}
|
||||||
data-disabled={
|
data-disabled={disabled}
|
||||||
(updateAvailable?.allowAutoUpdate && !updateReady) || appQuitting
|
|
||||||
}
|
|
||||||
onClick={onClickUpdate}
|
onClick={onClickUpdate}
|
||||||
>
|
>
|
||||||
{updateAvailableNode}
|
{ContentComponent ? <ContentComponent {...contentProps} /> : null}
|
||||||
{whatsNew}
|
|
||||||
<div className={styles.particles} aria-hidden="true"></div>
|
<div className={styles.particles} aria-hidden="true"></div>
|
||||||
<span className={styles.halo} aria-hidden="true"></span>
|
<span className={styles.halo} aria-hidden="true"></span>
|
||||||
</button>,
|
</button>,
|
||||||
updateAvailable?.version
|
updateAvailable?.version
|
||||||
);
|
);
|
||||||
|
|
||||||
function renderUpdateAvailableAllowAutoUpdate() {
|
|
||||||
return (
|
|
||||||
<div className={clsx([styles.updateAvailableWrapper])}>
|
|
||||||
<div className={clsx([styles.installLabelNormal])}>
|
|
||||||
<span className={styles.ellipsisTextOverflow}>
|
|
||||||
{!updateReady
|
|
||||||
? t['com.affine.appUpdater.downloading']()
|
|
||||||
: t['com.affine.appUpdater.updateAvailable']()}
|
|
||||||
</span>
|
|
||||||
<span className={styles.versionLabel}>
|
|
||||||
{updateAvailable?.version}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{updateReady ? (
|
|
||||||
<div className={clsx([styles.installLabelHover])}>
|
|
||||||
<ResetIcon className={styles.icon} />
|
|
||||||
<span className={styles.ellipsisTextOverflow}>
|
|
||||||
{t[
|
|
||||||
appQuitting ? 'Loading' : 'com.affine.appUpdater.installUpdate'
|
|
||||||
]()}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className={styles.progress}>
|
|
||||||
<div
|
|
||||||
className={styles.progressInner}
|
|
||||||
style={{ width: `${downloadProgress}%` }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderUpdateAvailableNotAllowAutoUpdate() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className={clsx([styles.installLabelNormal])}>
|
|
||||||
<span className={styles.ellipsisTextOverflow}>
|
|
||||||
{t['com.affine.appUpdater.updateAvailable']()}
|
|
||||||
</span>
|
|
||||||
<span className={styles.versionLabel}>
|
|
||||||
{updateAvailable?.version}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={clsx([styles.installLabelHover])}>
|
|
||||||
<span className={styles.ellipsisTextOverflow}>
|
|
||||||
{t['com.affine.appUpdater.openDownloadPage']()}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderWhatsNew() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className={clsx([styles.whatsNewLabel])}>
|
|
||||||
<NewIcon className={styles.icon} />
|
|
||||||
<span className={styles.ellipsisTextOverflow}>
|
|
||||||
{t['com.affine.appUpdater.whatsNew']()}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={styles.closeIcon}
|
|
||||||
onClick={e => {
|
|
||||||
onDismissCurrentChangelog();
|
|
||||||
e.stopPropagation();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CloseIcon />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Although it is called an input, it is actually a button.
|
// Although it is called an input, it is actually a button.
|
||||||
@@ -178,62 +247,64 @@ export function AppUpdaterButton({
|
|||||||
className?: string;
|
className?: string;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
}) {
|
}) {
|
||||||
const currentChangelogUnread = useAtomValue(currentChangelogUnreadAtom);
|
const {
|
||||||
const updateReady = useAtomValue(updateReadyAtom);
|
quitAndInstall,
|
||||||
const updateAvailable = useAtomValue(updateAvailableAtom);
|
appQuitting,
|
||||||
const downloadProgress = useAtomValue(downloadProgressAtom);
|
autoDownload,
|
||||||
const currentVersion = useAtomValue(currentVersionAtom);
|
downloadUpdate,
|
||||||
const { quitAndInstall, appQuitting } = useAppUpdater();
|
readChangelog,
|
||||||
const setChangelogCheckAtom = useSetAtom(changelogCheckedAtom);
|
changelogUnread,
|
||||||
|
updateReady,
|
||||||
const dismissCurrentChangelog = useCallback(() => {
|
updateAvailable,
|
||||||
if (!currentVersion) {
|
downloadProgress,
|
||||||
return;
|
currentVersion,
|
||||||
}
|
} = useAppUpdater();
|
||||||
startTransition(() =>
|
|
||||||
setChangelogCheckAtom(mapping => {
|
|
||||||
return {
|
|
||||||
...mapping,
|
|
||||||
[currentVersion]: true,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}, [currentVersion, setChangelogCheckAtom]);
|
|
||||||
|
|
||||||
const handleClickUpdate = useCallback(() => {
|
const handleClickUpdate = useCallback(() => {
|
||||||
if (updateReady) {
|
if (updateReady) {
|
||||||
quitAndInstall();
|
quitAndInstall();
|
||||||
} else if (updateAvailable) {
|
} else if (updateAvailable) {
|
||||||
if (updateAvailable.allowAutoUpdate) {
|
if (updateAvailable.allowAutoUpdate) {
|
||||||
// wait for download to finish
|
if (autoDownload) {
|
||||||
|
// wait for download to finish
|
||||||
|
} else {
|
||||||
|
downloadUpdate();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
window.open(
|
window.open(
|
||||||
`https://github.com/toeverything/AFFiNE/releases/tag/v${currentVersion}`,
|
`https://github.com/toeverything/AFFiNE/releases/tag/v${currentVersion}`,
|
||||||
'_blank'
|
'_blank'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (currentChangelogUnread) {
|
} else if (changelogUnread) {
|
||||||
window.open(runtimeConfig.changelogUrl, '_blank');
|
window.open(runtimeConfig.changelogUrl, '_blank');
|
||||||
dismissCurrentChangelog();
|
readChangelog();
|
||||||
} else {
|
} else {
|
||||||
throw new Unreachable();
|
throw new Unreachable();
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
updateReady,
|
updateReady,
|
||||||
quitAndInstall,
|
|
||||||
updateAvailable,
|
updateAvailable,
|
||||||
currentChangelogUnread,
|
changelogUnread,
|
||||||
dismissCurrentChangelog,
|
quitAndInstall,
|
||||||
|
autoDownload,
|
||||||
|
downloadUpdate,
|
||||||
currentVersion,
|
currentVersion,
|
||||||
|
readChangelog,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (!updateAvailable && !changelogUnread) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppUpdaterButtonPure
|
<AppUpdaterButtonPure
|
||||||
appQuitting={appQuitting}
|
appQuitting={appQuitting}
|
||||||
|
autoDownload={autoDownload}
|
||||||
updateReady={!!updateReady}
|
updateReady={!!updateReady}
|
||||||
onClickUpdate={handleClickUpdate}
|
onClickUpdate={handleClickUpdate}
|
||||||
onDismissCurrentChangelog={dismissCurrentChangelog}
|
onDismissCurrentChangelog={readChangelog}
|
||||||
currentChangelogUnread={currentChangelogUnread}
|
currentChangelogUnread={changelogUnread}
|
||||||
updateAvailable={updateAvailable}
|
updateAvailable={updateAvailable}
|
||||||
downloadProgress={downloadProgress}
|
downloadProgress={downloadProgress}
|
||||||
className={className}
|
className={className}
|
||||||
|
|||||||
@@ -62,6 +62,7 @@
|
|||||||
"intl-segmenter-polyfill-rs": "^0.1.6",
|
"intl-segmenter-polyfill-rs": "^0.1.6",
|
||||||
"jotai": "^2.5.1",
|
"jotai": "^2.5.1",
|
||||||
"jotai-devtools": "^0.7.0",
|
"jotai-devtools": "^0.7.0",
|
||||||
|
"jotai-effect": "^0.2.3",
|
||||||
"lit": "^3.0.2",
|
"lit": "^3.0.2",
|
||||||
"lottie-web": "^5.12.2",
|
"lottie-web": "^5.12.2",
|
||||||
"mini-css-extract-plugin": "^2.7.6",
|
"mini-css-extract-plugin": "^2.7.6",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import type { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { SettingsIcon } from '@blocksuite/icons';
|
import { SettingsIcon } from '@blocksuite/icons';
|
||||||
|
import { appSettingAtom } from '@toeverything/infra/atom';
|
||||||
import {
|
import {
|
||||||
PreconditionStrategy,
|
PreconditionStrategy,
|
||||||
registerAffineCommand,
|
registerAffineCommand,
|
||||||
@@ -8,7 +9,6 @@ import { type createStore } from 'jotai';
|
|||||||
import type { useTheme } from 'next-themes';
|
import type { useTheme } from 'next-themes';
|
||||||
|
|
||||||
import { openQuickSearchModalAtom } from '../atoms';
|
import { openQuickSearchModalAtom } from '../atoms';
|
||||||
import { appSettingAtom } from '../atoms/settings';
|
|
||||||
import type { useLanguageHelper } from '../hooks/affine/use-language-helper';
|
import type { useLanguageHelper } from '../hooks/affine/use-language-helper';
|
||||||
|
|
||||||
export function registerAffineSettingsCommands({
|
export function registerAffineSettingsCommands({
|
||||||
|
|||||||
+21
-26
@@ -3,15 +3,8 @@ import { SettingRow } from '@affine/component/setting-components';
|
|||||||
import { Button } from '@affine/component/ui/button';
|
import { Button } from '@affine/component/ui/button';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks';
|
||||||
import {
|
import { useAppUpdater } from '@toeverything/hooks/use-app-updater';
|
||||||
downloadProgressAtom,
|
|
||||||
isCheckingForUpdatesAtom,
|
|
||||||
updateAvailableAtom,
|
|
||||||
updateReadyAtom,
|
|
||||||
useAppUpdater,
|
|
||||||
} from '@toeverything/hooks/use-app-updater';
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useAtomValue } from 'jotai';
|
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import * as styles from './style.css';
|
import * as styles from './style.css';
|
||||||
@@ -25,18 +18,16 @@ enum CheckUpdateStatus {
|
|||||||
|
|
||||||
const useUpdateStatusLabels = (checkUpdateStatus: CheckUpdateStatus) => {
|
const useUpdateStatusLabels = (checkUpdateStatus: CheckUpdateStatus) => {
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const isCheckingForUpdates = useAtomValue(isCheckingForUpdatesAtom);
|
const { updateAvailable, downloadProgress, updateReady, checkingForUpdates } =
|
||||||
const updateAvailable = useAtomValue(updateAvailableAtom);
|
useAppUpdater();
|
||||||
const updateReady = useAtomValue(updateReadyAtom);
|
|
||||||
const downloadProgress = useAtomValue(downloadProgressAtom);
|
|
||||||
|
|
||||||
const buttonLabel = useMemo(() => {
|
const buttonLabel = useMemo(() => {
|
||||||
if (updateAvailable && downloadProgress === null) {
|
|
||||||
return t['com.affine.aboutAFFiNE.checkUpdate.button.download']();
|
|
||||||
}
|
|
||||||
if (updateReady) {
|
if (updateReady) {
|
||||||
return t['com.affine.aboutAFFiNE.checkUpdate.button.restart']();
|
return t['com.affine.aboutAFFiNE.checkUpdate.button.restart']();
|
||||||
}
|
}
|
||||||
|
if (updateAvailable && downloadProgress === null) {
|
||||||
|
return t['com.affine.aboutAFFiNE.checkUpdate.button.download']();
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
checkUpdateStatus === CheckUpdateStatus.LATEST ||
|
checkUpdateStatus === CheckUpdateStatus.LATEST ||
|
||||||
checkUpdateStatus === CheckUpdateStatus.ERROR
|
checkUpdateStatus === CheckUpdateStatus.ERROR
|
||||||
@@ -47,16 +38,16 @@ const useUpdateStatusLabels = (checkUpdateStatus: CheckUpdateStatus) => {
|
|||||||
}, [checkUpdateStatus, downloadProgress, t, updateAvailable, updateReady]);
|
}, [checkUpdateStatus, downloadProgress, t, updateAvailable, updateReady]);
|
||||||
|
|
||||||
const subtitleLabel = useMemo(() => {
|
const subtitleLabel = useMemo(() => {
|
||||||
if (updateAvailable && downloadProgress === null) {
|
if (updateReady) {
|
||||||
|
return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.restart']();
|
||||||
|
} else if (updateAvailable && downloadProgress === null) {
|
||||||
return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.update-available']({
|
return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.update-available']({
|
||||||
version: updateAvailable.version,
|
version: updateAvailable.version,
|
||||||
});
|
});
|
||||||
} else if (isCheckingForUpdates) {
|
} else if (checkingForUpdates) {
|
||||||
return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.checking']();
|
return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.checking']();
|
||||||
} else if (updateAvailable && downloadProgress !== null) {
|
} else if (updateAvailable && downloadProgress !== null) {
|
||||||
return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.downloading']();
|
return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.downloading']();
|
||||||
} else if (updateReady) {
|
|
||||||
return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.restart']();
|
|
||||||
} else if (checkUpdateStatus === CheckUpdateStatus.ERROR) {
|
} else if (checkUpdateStatus === CheckUpdateStatus.ERROR) {
|
||||||
return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.error']();
|
return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.error']();
|
||||||
} else if (checkUpdateStatus === CheckUpdateStatus.LATEST) {
|
} else if (checkUpdateStatus === CheckUpdateStatus.LATEST) {
|
||||||
@@ -66,7 +57,7 @@ const useUpdateStatusLabels = (checkUpdateStatus: CheckUpdateStatus) => {
|
|||||||
}, [
|
}, [
|
||||||
checkUpdateStatus,
|
checkUpdateStatus,
|
||||||
downloadProgress,
|
downloadProgress,
|
||||||
isCheckingForUpdates,
|
checkingForUpdates,
|
||||||
t,
|
t,
|
||||||
updateAvailable,
|
updateAvailable,
|
||||||
updateReady,
|
updateReady,
|
||||||
@@ -83,14 +74,14 @@ const useUpdateStatusLabels = (checkUpdateStatus: CheckUpdateStatus) => {
|
|||||||
error: checkUpdateStatus === CheckUpdateStatus.ERROR,
|
error: checkUpdateStatus === CheckUpdateStatus.ERROR,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{isCheckingForUpdates ? <Loading size={14} /> : null}
|
{checkingForUpdates ? <Loading size={14} /> : null}
|
||||||
{subtitleLabel}
|
{subtitleLabel}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}, [
|
}, [
|
||||||
checkUpdateStatus,
|
checkUpdateStatus,
|
||||||
downloadProgress,
|
downloadProgress,
|
||||||
isCheckingForUpdates,
|
checkingForUpdates,
|
||||||
subtitleLabel,
|
subtitleLabel,
|
||||||
updateAvailable,
|
updateAvailable,
|
||||||
updateReady,
|
updateReady,
|
||||||
@@ -101,10 +92,14 @@ const useUpdateStatusLabels = (checkUpdateStatus: CheckUpdateStatus) => {
|
|||||||
|
|
||||||
export const UpdateCheckSection = () => {
|
export const UpdateCheckSection = () => {
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const { checkForUpdates, downloadUpdate, quitAndInstall } = useAppUpdater();
|
const {
|
||||||
const updateAvailable = useAtomValue(updateAvailableAtom);
|
checkForUpdates,
|
||||||
const updateReady = useAtomValue(updateReadyAtom);
|
downloadUpdate,
|
||||||
const downloadProgress = useAtomValue(downloadProgressAtom);
|
quitAndInstall,
|
||||||
|
updateAvailable,
|
||||||
|
downloadProgress,
|
||||||
|
updateReady,
|
||||||
|
} = useAppUpdater();
|
||||||
const [checkUpdateStatus, setCheckUpdateStatus] = useState<CheckUpdateStatus>(
|
const [checkUpdateStatus, setCheckUpdateStatus] = useState<CheckUpdateStatus>(
|
||||||
CheckUpdateStatus.UNCHECK
|
CheckUpdateStatus.UNCHECK
|
||||||
);
|
);
|
||||||
|
|||||||
+1
-4
@@ -1,11 +1,8 @@
|
|||||||
import { Menu, MenuItem, MenuTrigger } from '@affine/component/ui/menu';
|
import { Menu, MenuItem, MenuTrigger } from '@affine/component/ui/menu';
|
||||||
|
import { dateFormatOptions, type DateFormats } from '@toeverything/infra/atom';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import {
|
|
||||||
dateFormatOptions,
|
|
||||||
type DateFormats,
|
|
||||||
} from '../../../../../atoms/settings';
|
|
||||||
import { useAppSettingHelper } from '../../../../../hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '../../../../../hooks/affine/use-app-setting-helper';
|
||||||
|
|
||||||
interface DateFormatMenuContentProps {
|
interface DateFormatMenuContentProps {
|
||||||
|
|||||||
+4
-4
@@ -3,14 +3,14 @@ import { SettingHeader } from '@affine/component/setting-components';
|
|||||||
import { SettingRow } from '@affine/component/setting-components';
|
import { SettingRow } from '@affine/component/setting-components';
|
||||||
import { SettingWrapper } from '@affine/component/setting-components';
|
import { SettingWrapper } from '@affine/component/setting-components';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { useTheme } from 'next-themes';
|
|
||||||
import { useCallback } from 'react';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type AppSetting,
|
type AppSetting,
|
||||||
fontStyleOptions,
|
fontStyleOptions,
|
||||||
windowFrameStyleOptions,
|
windowFrameStyleOptions,
|
||||||
} from '../../../../../atoms/settings';
|
} from '@toeverything/infra/atom';
|
||||||
|
import { useTheme } from 'next-themes';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { useAppSettingHelper } from '../../../../../hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '../../../../../hooks/affine/use-app-setting-helper';
|
||||||
import { LanguageMenu } from '../../../language-menu';
|
import { LanguageMenu } from '../../../language-menu';
|
||||||
import { DateFormatSetting } from './date-format-setting';
|
import { DateFormatSetting } from './date-format-setting';
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-
|
|||||||
import { useBlockSuiteWorkspacePage } from '@toeverything/hooks/use-block-suite-workspace-page';
|
import { useBlockSuiteWorkspacePage } from '@toeverything/hooks/use-block-suite-workspace-page';
|
||||||
import { pluginEditorAtom } from '@toeverything/infra/__internal__/plugin';
|
import { pluginEditorAtom } from '@toeverything/infra/__internal__/plugin';
|
||||||
import { getCurrentStore } from '@toeverything/infra/atom';
|
import { getCurrentStore } from '@toeverything/infra/atom';
|
||||||
|
import { fontStyleOptions } from '@toeverything/infra/atom';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
import type { CSSProperties } from 'react';
|
import type { CSSProperties } from 'react';
|
||||||
@@ -15,7 +16,6 @@ import { memo, Suspense, useCallback, useMemo, useState } from 'react';
|
|||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
import { type PageMode, pageSettingFamily } from '../atoms';
|
import { type PageMode, pageSettingFamily } from '../atoms';
|
||||||
import { fontStyleOptions } from '../atoms/settings';
|
|
||||||
import { useAppSettingHelper } from '../hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '../hooks/affine/use-app-setting-helper';
|
||||||
import { useBlockSuiteMetaHelper } from '../hooks/affine/use-block-suite-meta-helper';
|
import { useBlockSuiteMetaHelper } from '../hooks/affine/use-block-suite-meta-helper';
|
||||||
import { BlockSuiteEditor as Editor } from './blocksuite/block-suite-editor';
|
import { BlockSuiteEditor as Editor } from './blocksuite/block-suite-editor';
|
||||||
|
|||||||
@@ -20,11 +20,6 @@ import { FolderIcon, SettingsIcon } from '@blocksuite/icons';
|
|||||||
import type { Page } from '@blocksuite/store';
|
import type { Page } from '@blocksuite/store';
|
||||||
import { useDroppable } from '@dnd-kit/core';
|
import { useDroppable } from '@dnd-kit/core';
|
||||||
import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks';
|
||||||
import {
|
|
||||||
isAutoCheckUpdateAtom,
|
|
||||||
isAutoDownloadUpdateAtom,
|
|
||||||
useAppUpdater,
|
|
||||||
} from '@toeverything/hooks/use-app-updater';
|
|
||||||
import { useAtom, useAtomValue } from 'jotai';
|
import { useAtom, useAtomValue } from 'jotai';
|
||||||
import type { HTMLAttributes, ReactElement } from 'react';
|
import type { HTMLAttributes, ReactElement } from 'react';
|
||||||
import { forwardRef, useCallback, useEffect, useMemo } from 'react';
|
import { forwardRef, useCallback, useEffect, useMemo } from 'react';
|
||||||
@@ -107,10 +102,6 @@ export const RootAppSidebar = ({
|
|||||||
}: RootAppSidebarProps): ReactElement => {
|
}: RootAppSidebarProps): ReactElement => {
|
||||||
const currentWorkspaceId = currentWorkspace.id;
|
const currentWorkspaceId = currentWorkspace.id;
|
||||||
const { appSettings } = useAppSettingHelper();
|
const { appSettings } = useAppSettingHelper();
|
||||||
const { toggleAutoCheck, toggleAutoDownload } = useAppUpdater();
|
|
||||||
const { autoCheckUpdate, autoDownloadUpdate } = appSettings;
|
|
||||||
const isAutoDownload = useAtomValue(isAutoDownloadUpdateAtom);
|
|
||||||
const isAutoCheck = useAtomValue(isAutoCheckUpdateAtom);
|
|
||||||
const blockSuiteWorkspace = currentWorkspace.blockSuiteWorkspace;
|
const blockSuiteWorkspace = currentWorkspace.blockSuiteWorkspace;
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const [openUserWorkspaceList, setOpenUserWorkspaceList] = useAtom(
|
const [openUserWorkspaceList, setOpenUserWorkspaceList] = useAtom(
|
||||||
@@ -159,26 +150,6 @@ export const RootAppSidebar = ({
|
|||||||
}
|
}
|
||||||
}, [sidebarOpen]);
|
}, [sidebarOpen]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!environment.isDesktop) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAutoCheck !== autoCheckUpdate) {
|
|
||||||
toggleAutoCheck(autoCheckUpdate);
|
|
||||||
}
|
|
||||||
if (isAutoDownload !== autoDownloadUpdate) {
|
|
||||||
toggleAutoDownload(autoDownloadUpdate);
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
autoCheckUpdate,
|
|
||||||
autoDownloadUpdate,
|
|
||||||
isAutoCheck,
|
|
||||||
isAutoDownload,
|
|
||||||
toggleAutoCheck,
|
|
||||||
toggleAutoDownload,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [history, setHistory] = useHistoryAtom();
|
const [history, setHistory] = useHistoryAtom();
|
||||||
const router = useMemo(() => {
|
const router = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
|
import { type AppSetting, appSettingAtom } from '@toeverything/infra/atom';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import { type AppSetting, appSettingAtom } from '../../atoms/settings';
|
|
||||||
|
|
||||||
export function useAppSettingHelper() {
|
export function useAppSettingHelper() {
|
||||||
const [appSettings, setAppSettings] = useAtom(appSettingAtom);
|
const [appSettings, setAppSettings] = useAtom(appSettingAtom);
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import type { EditorContainer } from '@blocksuite/presets';
|
|||||||
import type { Page, Workspace } from '@blocksuite/store';
|
import type { Page, Workspace } from '@blocksuite/store';
|
||||||
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
|
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
|
||||||
import {
|
import {
|
||||||
|
appSettingAtom,
|
||||||
currentPageIdAtom,
|
currentPageIdAtom,
|
||||||
currentWorkspaceIdAtom,
|
currentWorkspaceIdAtom,
|
||||||
} from '@toeverything/infra/atom';
|
} from '@toeverything/infra/atom';
|
||||||
@@ -36,7 +37,6 @@ import type { Map as YMap } from 'yjs';
|
|||||||
import { setPageModeAtom } from '../../../atoms';
|
import { setPageModeAtom } from '../../../atoms';
|
||||||
import { collectionsCRUDAtom } from '../../../atoms/collections';
|
import { collectionsCRUDAtom } from '../../../atoms/collections';
|
||||||
import { currentModeAtom } from '../../../atoms/mode';
|
import { currentModeAtom } from '../../../atoms/mode';
|
||||||
import { appSettingAtom } from '../../../atoms/settings';
|
|
||||||
import { AffineErrorBoundary } from '../../../components/affine/affine-error-boundary';
|
import { AffineErrorBoundary } from '../../../components/affine/affine-error-boundary';
|
||||||
import { HubIsland } from '../../../components/affine/hub-island';
|
import { HubIsland } from '../../../components/affine/hub-island';
|
||||||
import { GlobalPageHistoryModal } from '../../../components/affine/page-history-modal';
|
import { GlobalPageHistoryModal } from '../../../components/affine/page-history-modal';
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ export function createApplicationMenu() {
|
|||||||
{
|
{
|
||||||
label: 'Check for Updates',
|
label: 'Check for Updates',
|
||||||
click: async () => {
|
click: async () => {
|
||||||
await checkForUpdates(true);
|
await checkForUpdates();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ export const quitAndInstall = async () => {
|
|||||||
autoUpdater.quitAndInstall();
|
autoUpdater.quitAndInstall();
|
||||||
};
|
};
|
||||||
|
|
||||||
let lastCheckTime = 0;
|
|
||||||
|
|
||||||
let downloading = false;
|
let downloading = false;
|
||||||
|
let configured = false;
|
||||||
|
let checkingUpdate = false;
|
||||||
|
|
||||||
export type UpdaterConfig = {
|
export type UpdaterConfig = {
|
||||||
autoCheckUpdate: boolean;
|
autoCheckUpdate: boolean;
|
||||||
@@ -36,29 +36,39 @@ export const getConfig = (): UpdaterConfig => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const setConfig = (newConfig: Partial<UpdaterConfig> = {}): void => {
|
export const setConfig = (newConfig: Partial<UpdaterConfig> = {}): void => {
|
||||||
|
configured = true;
|
||||||
|
|
||||||
Object.assign(config, newConfig);
|
Object.assign(config, newConfig);
|
||||||
|
|
||||||
|
logger.info('Updater configured!', config);
|
||||||
|
|
||||||
|
// if config.autoCheckUpdate is true, trigger a check
|
||||||
|
if (config.autoCheckUpdate) {
|
||||||
|
checkForUpdates().catch(err => {
|
||||||
|
logger.error('Error checking for updates', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const checkForUpdates = async (force = false) => {
|
export const checkForUpdates = async () => {
|
||||||
if (disabled) {
|
if (disabled || checkingUpdate) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
checkingUpdate = true;
|
||||||
if (
|
try {
|
||||||
force ||
|
const info = await autoUpdater.checkForUpdates();
|
||||||
(config.autoCheckUpdate && lastCheckTime + 1000 * 1800 < Date.now())
|
return info;
|
||||||
) {
|
} finally {
|
||||||
lastCheckTime = Date.now();
|
checkingUpdate = false;
|
||||||
return await autoUpdater.checkForUpdates();
|
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const downloadUpdate = async () => {
|
export const downloadUpdate = async () => {
|
||||||
if (disabled) {
|
if (disabled || downloading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
downloading = true;
|
downloading = true;
|
||||||
|
updaterSubjects.downloadProgress.next(0);
|
||||||
autoUpdater.downloadUpdate().catch(e => {
|
autoUpdater.downloadUpdate().catch(e => {
|
||||||
downloading = false;
|
downloading = false;
|
||||||
logger.error('Failed to download update', e);
|
logger.error('Failed to download update', e);
|
||||||
@@ -96,19 +106,16 @@ export const registerUpdater = async () => {
|
|||||||
|
|
||||||
autoUpdater.setFeedURL(feedUrl);
|
autoUpdater.setFeedURL(feedUrl);
|
||||||
|
|
||||||
// register events for checkForUpdatesAndNotify
|
// register events for checkForUpdates
|
||||||
autoUpdater.on('checking-for-update', () => {
|
autoUpdater.on('checking-for-update', () => {
|
||||||
logger.info('Checking for update');
|
logger.info('Checking for update');
|
||||||
});
|
});
|
||||||
autoUpdater.on('update-available', info => {
|
autoUpdater.on('update-available', info => {
|
||||||
logger.info('Update available', info);
|
logger.info('Update available', info);
|
||||||
if (config.autoDownloadUpdate && allowAutoUpdate && !downloading) {
|
if (config.autoDownloadUpdate && allowAutoUpdate) {
|
||||||
downloading = true;
|
downloadUpdate().catch(err => {
|
||||||
autoUpdater?.downloadUpdate().catch(e => {
|
console.error(err);
|
||||||
downloading = false;
|
|
||||||
logger.error('Failed to download update', e);
|
|
||||||
});
|
});
|
||||||
logger.info('Update available, downloading...', info);
|
|
||||||
}
|
}
|
||||||
updaterSubjects.updateAvailable.next({
|
updaterSubjects.updateAvailable.next({
|
||||||
version: info.version,
|
version: info.version,
|
||||||
@@ -137,9 +144,20 @@ export const registerUpdater = async () => {
|
|||||||
});
|
});
|
||||||
autoUpdater.forceDevUpdateConfig = isDev;
|
autoUpdater.forceDevUpdateConfig = isDev;
|
||||||
|
|
||||||
|
// check update whenever the window is activated
|
||||||
|
let lastCheckTime = 0;
|
||||||
app.on('activate', () => {
|
app.on('activate', () => {
|
||||||
checkForUpdates(false).catch(err => {
|
(async () => {
|
||||||
console.error(err);
|
if (
|
||||||
|
configured &&
|
||||||
|
config.autoCheckUpdate &&
|
||||||
|
lastCheckTime + 1000 * 1800 < Date.now()
|
||||||
|
) {
|
||||||
|
lastCheckTime = Date.now();
|
||||||
|
await checkForUpdates();
|
||||||
|
}
|
||||||
|
})().catch(err => {
|
||||||
|
logger.error('Error checking for updates', err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ export const updaterHandlers = {
|
|||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
return setConfig(newConfig);
|
return setConfig(newConfig);
|
||||||
},
|
},
|
||||||
checkForUpdatesAndNotify: async () => {
|
checkForUpdates: async () => {
|
||||||
const res = await checkForUpdates(true);
|
const res = await checkForUpdates();
|
||||||
if (res) {
|
if (res) {
|
||||||
const { updateInfo } = res;
|
const { updateInfo } = res;
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"foxact": "^0.2.20",
|
"foxact": "^0.2.20",
|
||||||
"image-blob-reduce": "^4.1.0",
|
"image-blob-reduce": "^4.1.0",
|
||||||
"jotai": "^2.5.1",
|
"jotai": "^2.5.1",
|
||||||
|
"jotai-effect": "^0.2.3",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"p-queue": "^7.4.1",
|
"p-queue": "^7.4.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
"@blocksuite/presets": "0.11.0-nightly-202312070955-2b5bb47",
|
"@blocksuite/presets": "0.11.0-nightly-202312070955-2b5bb47",
|
||||||
"@blocksuite/store": "0.11.0-nightly-202312070955-2b5bb47",
|
"@blocksuite/store": "0.11.0-nightly-202312070955-2b5bb47",
|
||||||
"@testing-library/react": "^14.0.0",
|
"@testing-library/react": "^14.0.0",
|
||||||
|
"@toeverything/infra": "workspace:*",
|
||||||
"@types/image-blob-reduce": "^4.1.3",
|
"@types/image-blob-reduce": "^4.1.3",
|
||||||
"@types/lodash.debounce": "^4.0.7",
|
"@types/lodash.debounce": "^4.0.7",
|
||||||
"fake-indexeddb": "^5.0.0",
|
"fake-indexeddb": "^5.0.0",
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import { isBrowser } from '@affine/env/constant';
|
import { isBrowser } from '@affine/env/constant';
|
||||||
|
import { appSettingAtom } from '@toeverything/infra/atom';
|
||||||
import type { UpdateMeta } from '@toeverything/infra/type';
|
import type { UpdateMeta } from '@toeverything/infra/type';
|
||||||
import { atom, useAtomValue, useSetAtom } from 'jotai';
|
import { atom, useAtom, useAtomValue } from 'jotai';
|
||||||
import { atomWithObservable, atomWithStorage } from 'jotai/utils';
|
import { atomWithObservable, atomWithStorage } from 'jotai/utils';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { useAsyncCallback } from './affine-async-hooks';
|
||||||
|
|
||||||
function rpcToObservable<
|
function rpcToObservable<
|
||||||
T,
|
T,
|
||||||
H extends () => Promise<T>,
|
H extends () => Promise<T>,
|
||||||
@@ -41,25 +44,21 @@ function rpcToObservable<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// download complete, ready to install
|
||||||
export const updateReadyAtom = atomWithObservable(() => {
|
export const updateReadyAtom = atomWithObservable(() => {
|
||||||
return rpcToObservable(null as UpdateMeta | null, {
|
return rpcToObservable(null as UpdateMeta | null, {
|
||||||
event: window.events?.updater.onUpdateReady,
|
event: window.events?.updater.onUpdateReady,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
export const updateAvailableStateAtom = atom<UpdateMeta | null>(null);
|
// update available, but not downloaded yet
|
||||||
|
export const updateAvailableAtom = atomWithObservable(() => {
|
||||||
export const updateAvailableAtom = atomWithObservable(get => {
|
return rpcToObservable(null as UpdateMeta | null, {
|
||||||
return rpcToObservable(get(updateAvailableStateAtom), {
|
|
||||||
event: window.events?.updater.onUpdateAvailable,
|
event: window.events?.updater.onUpdateAvailable,
|
||||||
onSubscribe: () => {
|
|
||||||
window.apis?.updater.checkForUpdatesAndNotify().catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// downloading new update
|
||||||
export const downloadProgressAtom = atomWithObservable(() => {
|
export const downloadProgressAtom = atomWithObservable(() => {
|
||||||
return rpcToObservable(null as number | null, {
|
return rpcToObservable(null as number | null, {
|
||||||
event: window.events?.updater.onDownloadProgress,
|
event: window.events?.updater.onDownloadProgress,
|
||||||
@@ -71,6 +70,8 @@ export const changelogCheckedAtom = atomWithStorage<Record<string, boolean>>(
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const checkingForUpdatesAtom = atom(false);
|
||||||
|
|
||||||
export const currentVersionAtom = atom(async () => {
|
export const currentVersionAtom = atom(async () => {
|
||||||
if (!isBrowser) {
|
if (!isBrowser) {
|
||||||
return null;
|
return null;
|
||||||
@@ -79,29 +80,43 @@ export const currentVersionAtom = atom(async () => {
|
|||||||
return currentVersion;
|
return currentVersion;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const currentChangelogUnreadAtom = atom(async get => {
|
const currentChangelogUnreadAtom = atom(
|
||||||
if (!isBrowser) {
|
async get => {
|
||||||
|
if (!isBrowser) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const mapping = get(changelogCheckedAtom);
|
||||||
|
const currentVersion = await get(currentVersionAtom);
|
||||||
|
if (currentVersion) {
|
||||||
|
return !mapping[currentVersion];
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
|
},
|
||||||
|
async (get, set, v: boolean) => {
|
||||||
|
const currentVersion = await get(currentVersionAtom);
|
||||||
|
if (currentVersion) {
|
||||||
|
set(changelogCheckedAtom, mapping => {
|
||||||
|
return {
|
||||||
|
...mapping,
|
||||||
|
[currentVersion]: v,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const mapping = get(changelogCheckedAtom);
|
);
|
||||||
const currentVersion = await get(currentVersionAtom);
|
|
||||||
if (currentVersion) {
|
|
||||||
return !mapping[currentVersion];
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
export const isCheckingForUpdatesAtom = atom(false);
|
|
||||||
export const isAutoDownloadUpdateAtom = atom(true);
|
|
||||||
export const isAutoCheckUpdateAtom = atom(true);
|
|
||||||
|
|
||||||
export const useAppUpdater = () => {
|
export const useAppUpdater = () => {
|
||||||
const [appQuitting, setAppQuitting] = useState(false);
|
const [appQuitting, setAppQuitting] = useState(false);
|
||||||
const updateReady = useAtomValue(updateReadyAtom);
|
const updateReady = useAtomValue(updateReadyAtom);
|
||||||
const setUpdateAvailableState = useSetAtom(updateAvailableStateAtom);
|
const [setting, setSetting] = useAtom(appSettingAtom);
|
||||||
const setIsCheckingForUpdates = useSetAtom(isCheckingForUpdatesAtom);
|
const downloadProgress = useAtomValue(downloadProgressAtom);
|
||||||
const setIsAutoCheckUpdate = useSetAtom(isAutoCheckUpdateAtom);
|
const [changelogUnread, setChangelogUnread] = useAtom(
|
||||||
const setIsAutoDownloadUpdate = useSetAtom(isAutoDownloadUpdateAtom);
|
currentChangelogUnreadAtom
|
||||||
|
);
|
||||||
|
|
||||||
|
const [checkingForUpdates, setCheckingForUpdates] = useAtom(
|
||||||
|
checkingForUpdatesAtom
|
||||||
|
);
|
||||||
|
|
||||||
const quitAndInstall = useCallback(() => {
|
const quitAndInstall = useCallback(() => {
|
||||||
if (updateReady) {
|
if (updateReady) {
|
||||||
@@ -114,73 +129,64 @@ export const useAppUpdater = () => {
|
|||||||
}, [updateReady]);
|
}, [updateReady]);
|
||||||
|
|
||||||
const checkForUpdates = useCallback(async () => {
|
const checkForUpdates = useCallback(async () => {
|
||||||
setIsCheckingForUpdates(true);
|
if (checkingForUpdates) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setCheckingForUpdates(true);
|
||||||
try {
|
try {
|
||||||
const updateInfo = await window.apis?.updater.checkForUpdatesAndNotify();
|
const updateInfo = await window.apis?.updater.checkForUpdates();
|
||||||
setIsCheckingForUpdates(false);
|
return updateInfo?.version ?? false;
|
||||||
if (updateInfo) {
|
|
||||||
const updateMeta: UpdateMeta = {
|
|
||||||
version: updateInfo.version,
|
|
||||||
allowAutoUpdate: false,
|
|
||||||
};
|
|
||||||
setUpdateAvailableState(updateMeta);
|
|
||||||
return updateInfo.version;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setIsCheckingForUpdates(false);
|
|
||||||
console.error('Error checking for updates:', err);
|
console.error('Error checking for updates:', err);
|
||||||
return null;
|
return null;
|
||||||
|
} finally {
|
||||||
|
setCheckingForUpdates(false);
|
||||||
}
|
}
|
||||||
}, [setIsCheckingForUpdates, setUpdateAvailableState]);
|
}, [checkingForUpdates, setCheckingForUpdates]);
|
||||||
|
|
||||||
const downloadUpdate = useCallback(() => {
|
const downloadUpdate = useCallback(() => {
|
||||||
window.apis?.updater
|
window.apis?.updater.downloadUpdate().catch(err => {
|
||||||
.downloadUpdate()
|
console.error('Error downloading update:', err);
|
||||||
.then(() => {})
|
});
|
||||||
.catch(err => {
|
|
||||||
console.error('Error downloading update:', err);
|
|
||||||
});
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const toggleAutoDownload = useCallback(
|
const toggleAutoDownload = useCallback(
|
||||||
(enable: boolean) => {
|
(enable: boolean) => {
|
||||||
window.apis?.updater
|
setSetting({
|
||||||
.setConfig({
|
autoDownloadUpdate: enable,
|
||||||
autoDownloadUpdate: enable,
|
});
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
setIsAutoDownloadUpdate(enable);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error('Error setting auto download:', err);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[setIsAutoDownloadUpdate]
|
[setSetting]
|
||||||
);
|
);
|
||||||
|
|
||||||
const toggleAutoCheck = useCallback(
|
const toggleAutoCheck = useCallback(
|
||||||
(enable: boolean) => {
|
(enable: boolean) => {
|
||||||
window.apis?.updater
|
setSetting({
|
||||||
.setConfig({
|
autoCheckUpdate: enable,
|
||||||
autoCheckUpdate: enable,
|
});
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
setIsAutoCheckUpdate(enable);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error('Error setting auto check:', err);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[setIsAutoCheckUpdate]
|
[setSetting]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const readChangelog = useAsyncCallback(async () => {
|
||||||
|
await setChangelogUnread(true);
|
||||||
|
}, [setChangelogUnread]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
quitAndInstall,
|
quitAndInstall,
|
||||||
appQuitting,
|
|
||||||
checkForUpdates,
|
checkForUpdates,
|
||||||
downloadUpdate,
|
downloadUpdate,
|
||||||
toggleAutoDownload,
|
toggleAutoDownload,
|
||||||
toggleAutoCheck,
|
toggleAutoCheck,
|
||||||
|
appQuitting,
|
||||||
|
checkingForUpdates,
|
||||||
|
autoCheck: setting.autoCheckUpdate,
|
||||||
|
autoDownload: setting.autoDownloadUpdate,
|
||||||
|
changelogUnread,
|
||||||
|
readChangelog,
|
||||||
|
updateReady,
|
||||||
|
updateAvailable: useAtomValue(updateAvailableAtom),
|
||||||
|
downloadProgress,
|
||||||
|
currentVersion: useAtomValue(currentVersionAtom),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"references": [
|
"references": [
|
||||||
{ "path": "../../common/env" },
|
{ "path": "../../common/env" },
|
||||||
{ "path": "../../common/y-indexeddb" },
|
{ "path": "../../common/y-indexeddb" },
|
||||||
{ "path": "../../common/debug" }
|
{ "path": "../../common/debug" },
|
||||||
|
{ "path": "../../common/infra" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -373,6 +373,7 @@
|
|||||||
"com.affine.appUpdater.downloading": "Downloading",
|
"com.affine.appUpdater.downloading": "Downloading",
|
||||||
"com.affine.appUpdater.installUpdate": "Restart to install update",
|
"com.affine.appUpdater.installUpdate": "Restart to install update",
|
||||||
"com.affine.appUpdater.openDownloadPage": "Open download page",
|
"com.affine.appUpdater.openDownloadPage": "Open download page",
|
||||||
|
"com.affine.appUpdater.downloadUpdate": "Download update",
|
||||||
"com.affine.appUpdater.updateAvailable": "Update available",
|
"com.affine.appUpdater.updateAvailable": "Update available",
|
||||||
"com.affine.appUpdater.whatsNew": "Discover what's new!",
|
"com.affine.appUpdater.whatsNew": "Discover what's new!",
|
||||||
"com.affine.appearanceSettings.clientBorder.description": "Customise the appearance of the client.",
|
"com.affine.appearanceSettings.clientBorder.description": "Customise the appearance of the client.",
|
||||||
|
|||||||
@@ -402,6 +402,7 @@ __metadata:
|
|||||||
intl-segmenter-polyfill-rs: "npm:^0.1.6"
|
intl-segmenter-polyfill-rs: "npm:^0.1.6"
|
||||||
jotai: "npm:^2.5.1"
|
jotai: "npm:^2.5.1"
|
||||||
jotai-devtools: "npm:^0.7.0"
|
jotai-devtools: "npm:^0.7.0"
|
||||||
|
jotai-effect: "npm:^0.2.3"
|
||||||
lit: "npm:^3.0.2"
|
lit: "npm:^3.0.2"
|
||||||
lodash-es: "npm:^4.17.21"
|
lodash-es: "npm:^4.17.21"
|
||||||
lottie-web: "npm:^5.12.2"
|
lottie-web: "npm:^5.12.2"
|
||||||
@@ -524,7 +525,6 @@ __metadata:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@affine/templates": "workspace:*"
|
"@affine/templates": "workspace:*"
|
||||||
"@blocksuite/global": 0.0.0-20230409084303-221991d4-nightly
|
"@blocksuite/global": 0.0.0-20230409084303-221991d4-nightly
|
||||||
"@toeverything/infra": "workspace:*"
|
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
@@ -13665,12 +13665,14 @@ __metadata:
|
|||||||
"@blocksuite/presets": "npm:0.11.0-nightly-202312070955-2b5bb47"
|
"@blocksuite/presets": "npm:0.11.0-nightly-202312070955-2b5bb47"
|
||||||
"@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47"
|
"@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47"
|
||||||
"@testing-library/react": "npm:^14.0.0"
|
"@testing-library/react": "npm:^14.0.0"
|
||||||
|
"@toeverything/infra": "workspace:*"
|
||||||
"@types/image-blob-reduce": "npm:^4.1.3"
|
"@types/image-blob-reduce": "npm:^4.1.3"
|
||||||
"@types/lodash.debounce": "npm:^4.0.7"
|
"@types/lodash.debounce": "npm:^4.0.7"
|
||||||
fake-indexeddb: "npm:^5.0.0"
|
fake-indexeddb: "npm:^5.0.0"
|
||||||
foxact: "npm:^0.2.20"
|
foxact: "npm:^0.2.20"
|
||||||
image-blob-reduce: "npm:^4.1.0"
|
image-blob-reduce: "npm:^4.1.0"
|
||||||
jotai: "npm:^2.5.1"
|
jotai: "npm:^2.5.1"
|
||||||
|
jotai-effect: "npm:^0.2.3"
|
||||||
lodash.debounce: "npm:^4.0.8"
|
lodash.debounce: "npm:^4.0.8"
|
||||||
p-queue: "npm:^7.4.1"
|
p-queue: "npm:^7.4.1"
|
||||||
react: "npm:18.2.0"
|
react: "npm:18.2.0"
|
||||||
@@ -13709,6 +13711,7 @@ __metadata:
|
|||||||
resolution: "@toeverything/infra@workspace:packages/common/infra"
|
resolution: "@toeverything/infra@workspace:packages/common/infra"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@affine-test/fixtures": "workspace:*"
|
"@affine-test/fixtures": "workspace:*"
|
||||||
|
"@affine/env": "workspace:*"
|
||||||
"@affine/sdk": "workspace:*"
|
"@affine/sdk": "workspace:*"
|
||||||
"@affine/templates": "workspace:*"
|
"@affine/templates": "workspace:*"
|
||||||
"@blocksuite/blocks": "npm:0.11.0-nightly-202312070955-2b5bb47"
|
"@blocksuite/blocks": "npm:0.11.0-nightly-202312070955-2b5bb47"
|
||||||
|
|||||||
Reference in New Issue
Block a user