mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-06 08:50:50 +08:00
feat(core): add allowGuestDemoWorkspace flag to force login (#12779)
https://github.com/user-attachments/assets/41a659c9-6def-4492-be8e-5910eb148d6f This PR enforces login‑first access (#8716) by disabling or enabling the guest demo workspace via Admin Server Client Page and redirecting unauthenticated users straight to `/sign‑in`. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a configuration option to control whether guest users can create demo workspaces. * Updated server and client interfaces, GraphQL schema, and queries to support the new guest demo workspace flag. * **Bug Fixes** * Improved sign-out behavior to redirect users appropriately based on guest demo workspace permissions. * Enhanced navigation flow to handle guest demo workspace access and user authentication state. * **Tests** * Added tests to verify sign-out logic when guest demo workspaces are enabled or disabled. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: liuyi <forehalo@gmail.com> Co-authored-by: fengmk2 <fengmk2@gmail.com>
This commit is contained in:
@@ -565,6 +565,11 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Only allow users with early access features to access the app\n@default false",
|
"description": "Only allow users with early access features to access the app\n@default false",
|
||||||
"default": false
|
"default": false
|
||||||
|
},
|
||||||
|
"allowGuestDemoWorkspace": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether allow guest users to create demo workspaces.\n@default true",
|
||||||
|
"default": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -592,6 +597,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Allowed version range of the app that allowed to access the server. Requires 'client/versionControl.enabled' to be true to take effect.\n@default \">=0.20.0\"",
|
"description": "Allowed version range of the app that allowed to access the server. Requires 'client/versionControl.enabled' to be true to take effect.\n@default \">=0.20.0\"",
|
||||||
"default": ">=0.20.0"
|
"default": ">=0.20.0"
|
||||||
|
},
|
||||||
|
"allowGuestDemoWorkspace": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Allow guests to access demo workspace.\n@default true",
|
||||||
|
"default": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { defineModuleConfig } from '../../base';
|
|||||||
|
|
||||||
export interface ServerFlags {
|
export interface ServerFlags {
|
||||||
earlyAccessControl: boolean;
|
earlyAccessControl: boolean;
|
||||||
|
allowGuestDemoWorkspace: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -75,4 +76,8 @@ defineModuleConfig('flags', {
|
|||||||
desc: 'Only allow users with early access features to access the app',
|
desc: 'Only allow users with early access features to access the app',
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
allowGuestDemoWorkspace: {
|
||||||
|
desc: 'Whether allow guest users to create demo workspaces.',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ export class ServerConfigResolver {
|
|||||||
baseUrl: this.url.requestBaseUrl,
|
baseUrl: this.url.requestBaseUrl,
|
||||||
type: env.DEPLOYMENT_TYPE,
|
type: env.DEPLOYMENT_TYPE,
|
||||||
features: this.server.features,
|
features: this.server.features,
|
||||||
|
allowGuestDemoWorkspace: this.config.flags.allowGuestDemoWorkspace,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,4 +38,9 @@ export class ServerConfigType {
|
|||||||
|
|
||||||
@Field(() => [ServerFeature], { description: 'enabled server features' })
|
@Field(() => [ServerFeature], { description: 'enabled server features' })
|
||||||
features!: ServerFeature[];
|
features!: ServerFeature[];
|
||||||
|
|
||||||
|
@Field(() => Boolean, {
|
||||||
|
description: 'Whether allow guest users to create demo workspaces.',
|
||||||
|
})
|
||||||
|
allowGuestDemoWorkspace!: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export interface VersionConfig {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
requiredVersion: string;
|
requiredVersion: string;
|
||||||
};
|
};
|
||||||
|
allowGuestDemoWorkspace?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -28,4 +29,8 @@ defineModuleConfig('client', {
|
|||||||
desc: "Allowed version range of the app that allowed to access the server. Requires 'client/versionControl.enabled' to be true to take effect.",
|
desc: "Allowed version range of the app that allowed to access the server. Requires 'client/versionControl.enabled' to be true to take effect.",
|
||||||
default: '>=0.20.0',
|
default: '>=0.20.0',
|
||||||
},
|
},
|
||||||
|
allowGuestDemoWorkspace: {
|
||||||
|
desc: 'Allow guests to access demo workspace.',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1585,6 +1585,9 @@ enum SearchTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ServerConfigType {
|
type ServerConfigType {
|
||||||
|
"""Whether allow guest users to create demo workspaces."""
|
||||||
|
allowGuestDemoWorkspace: Boolean!
|
||||||
|
|
||||||
"""fetch latest available upgradable release of server"""
|
"""fetch latest available upgradable release of server"""
|
||||||
availableUpgrade: ReleaseVersionType
|
availableUpgrade: ReleaseVersionType
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ query adminServerConfig {
|
|||||||
baseUrl
|
baseUrl
|
||||||
name
|
name
|
||||||
features
|
features
|
||||||
|
allowGuestDemoWorkspace
|
||||||
type
|
type
|
||||||
initialized
|
initialized
|
||||||
credentialsRequirement {
|
credentialsRequirement {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export const adminServerConfigQuery = {
|
|||||||
baseUrl
|
baseUrl
|
||||||
name
|
name
|
||||||
features
|
features
|
||||||
|
allowGuestDemoWorkspace
|
||||||
type
|
type
|
||||||
initialized
|
initialized
|
||||||
credentialsRequirement {
|
credentialsRequirement {
|
||||||
@@ -1822,6 +1823,7 @@ export const serverConfigQuery = {
|
|||||||
baseUrl
|
baseUrl
|
||||||
name
|
name
|
||||||
features
|
features
|
||||||
|
allowGuestDemoWorkspace
|
||||||
type
|
type
|
||||||
initialized
|
initialized
|
||||||
credentialsRequirement {
|
credentialsRequirement {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ query serverConfig {
|
|||||||
baseUrl
|
baseUrl
|
||||||
name
|
name
|
||||||
features
|
features
|
||||||
|
allowGuestDemoWorkspace
|
||||||
type
|
type
|
||||||
initialized
|
initialized
|
||||||
credentialsRequirement {
|
credentialsRequirement {
|
||||||
|
|||||||
@@ -2149,6 +2149,8 @@ export enum SearchTable {
|
|||||||
|
|
||||||
export interface ServerConfigType {
|
export interface ServerConfigType {
|
||||||
__typename?: 'ServerConfigType';
|
__typename?: 'ServerConfigType';
|
||||||
|
/** Whether allow guest users to create demo workspaces. */
|
||||||
|
allowGuestDemoWorkspace: Scalars['Boolean']['output'];
|
||||||
/** fetch latest available upgradable release of server */
|
/** fetch latest available upgradable release of server */
|
||||||
availableUpgrade: Maybe<ReleaseVersionType>;
|
availableUpgrade: Maybe<ReleaseVersionType>;
|
||||||
/** Features for user that can be configured */
|
/** Features for user that can be configured */
|
||||||
@@ -2743,6 +2745,7 @@ export type AdminServerConfigQuery = {
|
|||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
name: string;
|
name: string;
|
||||||
features: Array<ServerFeature>;
|
features: Array<ServerFeature>;
|
||||||
|
allowGuestDemoWorkspace: boolean;
|
||||||
type: ServerDeploymentType;
|
type: ServerDeploymentType;
|
||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
availableUserFeatures: Array<FeatureType>;
|
availableUserFeatures: Array<FeatureType>;
|
||||||
@@ -4828,6 +4831,7 @@ export type ServerConfigQuery = {
|
|||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
name: string;
|
name: string;
|
||||||
features: Array<ServerFeature>;
|
features: Array<ServerFeature>;
|
||||||
|
allowGuestDemoWorkspace: boolean;
|
||||||
type: ServerDeploymentType;
|
type: ServerDeploymentType;
|
||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
credentialsRequirement: {
|
credentialsRequirement: {
|
||||||
|
|||||||
@@ -190,6 +190,10 @@
|
|||||||
"earlyAccessControl": {
|
"earlyAccessControl": {
|
||||||
"type": "Boolean",
|
"type": "Boolean",
|
||||||
"desc": "Only allow users with early access features to access the app"
|
"desc": "Only allow users with early access features to access the app"
|
||||||
|
},
|
||||||
|
"allowGuestDemoWorkspace": {
|
||||||
|
"type": "Boolean",
|
||||||
|
"desc": "Whether allow guest users to create demo workspaces."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"docService": {
|
"docService": {
|
||||||
@@ -207,6 +211,10 @@
|
|||||||
"versionControl.requiredVersion": {
|
"versionControl.requiredVersion": {
|
||||||
"type": "String",
|
"type": "String",
|
||||||
"desc": "Allowed version range of the app that allowed to access the server. Requires 'client/versionControl.enabled' to be true to take effect."
|
"desc": "Allowed version range of the app that allowed to access the server. Requires 'client/versionControl.enabled' to be true to take effect."
|
||||||
|
},
|
||||||
|
"allowGuestDemoWorkspace": {
|
||||||
|
"type": "Boolean",
|
||||||
|
"desc": "Allow guests to access demo workspace."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"captcha": {
|
"captcha": {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { useLiveData, useService } from '@toeverything/infra';
|
|||||||
import { useEffect, useMemo } from 'react';
|
import { useEffect, useMemo } from 'react';
|
||||||
|
|
||||||
import { AuthService, SubscriptionService } from '../../../modules/cloud';
|
import { AuthService, SubscriptionService } from '../../../modules/cloud';
|
||||||
|
import { useNavigateHelper } from '../../hooks/use-navigate-helper';
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
|
|
||||||
const UserInfo = () => {
|
const UserInfo = () => {
|
||||||
@@ -51,9 +52,11 @@ export const PublishPageUserAvatar = () => {
|
|||||||
const user = useLiveData(authService.session.account$);
|
const user = useLiveData(authService.session.account$);
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|
||||||
|
const navigateHelper = useNavigateHelper();
|
||||||
const handleSignOut = useAsyncCallback(async () => {
|
const handleSignOut = useAsyncCallback(async () => {
|
||||||
await authService.signOut();
|
await authService.signOut();
|
||||||
}, [authService]);
|
navigateHelper.jumpToSignIn();
|
||||||
|
}, [authService, navigateHelper]);
|
||||||
|
|
||||||
const menuItem = useMemo(() => {
|
const menuItem = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/* eslint-disable rxjs/finnish */
|
||||||
|
/**
|
||||||
|
* @vitest-environment happy-dom
|
||||||
|
*/
|
||||||
|
import { renderHook, waitFor } from '@testing-library/react';
|
||||||
|
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||||
|
|
||||||
|
// mocks
|
||||||
|
const signOutFn = vi.fn();
|
||||||
|
const jumpToIndex = vi.fn();
|
||||||
|
const jumpToSignIn = vi.fn();
|
||||||
|
let allowGuestDemo: boolean | undefined = true;
|
||||||
|
|
||||||
|
vi.mock('@affine/core/modules/cloud', () => ({
|
||||||
|
AuthService: class {},
|
||||||
|
DefaultServerService: class {},
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('@toeverything/infra', () => {
|
||||||
|
return {
|
||||||
|
useService: () => ({ signOut: signOutFn }),
|
||||||
|
useServices: () => ({
|
||||||
|
defaultServerService: {
|
||||||
|
server: {
|
||||||
|
config$: {
|
||||||
|
value: {
|
||||||
|
get allowGuestDemoWorkspace() {
|
||||||
|
return allowGuestDemo;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.mock('@affine/component', () => {
|
||||||
|
return {
|
||||||
|
useConfirmModal: () => ({
|
||||||
|
openConfirmModal: ({ onConfirm }: { onConfirm?: () => unknown }) => {
|
||||||
|
return Promise.resolve(onConfirm?.());
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
notify: { error: vi.fn() },
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.mock('@affine/i18n', () => ({
|
||||||
|
useI18n: () => new Proxy({}, { get: () => () => '' }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('../../use-navigate-helper', () => ({
|
||||||
|
useNavigateHelper: () => ({ jumpToIndex, jumpToSignIn }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { useSignOut } from '../use-sign-out';
|
||||||
|
|
||||||
|
describe('useSignOut', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
signOutFn.mockClear();
|
||||||
|
jumpToIndex.mockClear();
|
||||||
|
jumpToSignIn.mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('redirects to index when guest demo allowed', async () => {
|
||||||
|
allowGuestDemo = true;
|
||||||
|
const { result } = renderHook(() => useSignOut());
|
||||||
|
result.current();
|
||||||
|
await waitFor(() => expect(signOutFn).toHaveBeenCalled());
|
||||||
|
expect(jumpToIndex).toHaveBeenCalled();
|
||||||
|
expect(jumpToSignIn).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('redirects to index when guest demo config not provided', async () => {
|
||||||
|
allowGuestDemo = undefined;
|
||||||
|
const { result } = renderHook(() => useSignOut());
|
||||||
|
result.current();
|
||||||
|
await waitFor(() => expect(signOutFn).toHaveBeenCalled());
|
||||||
|
expect(jumpToIndex).toHaveBeenCalled();
|
||||||
|
expect(jumpToSignIn).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('redirects to sign in when guest demo disabled', async () => {
|
||||||
|
allowGuestDemo = false;
|
||||||
|
const { result } = renderHook(() => useSignOut());
|
||||||
|
result.current();
|
||||||
|
await waitFor(() => expect(signOutFn).toHaveBeenCalled());
|
||||||
|
expect(jumpToSignIn).toHaveBeenCalled();
|
||||||
|
expect(jumpToIndex).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -3,10 +3,10 @@ import {
|
|||||||
notify,
|
notify,
|
||||||
useConfirmModal,
|
useConfirmModal,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { AuthService } from '@affine/core/modules/cloud';
|
import { AuthService, DefaultServerService } from '@affine/core/modules/cloud';
|
||||||
import { UserFriendlyError } from '@affine/error';
|
import { UserFriendlyError } from '@affine/error';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { useService } from '@toeverything/infra';
|
import { useService, useServices } from '@toeverything/infra';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { useNavigateHelper } from '../use-navigate-helper';
|
import { useNavigateHelper } from '../use-navigate-helper';
|
||||||
@@ -25,21 +25,29 @@ export const useSignOut = ({
|
|||||||
}: ConfirmModalProps = {}) => {
|
}: ConfirmModalProps = {}) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const { openConfirmModal } = useConfirmModal();
|
const { openConfirmModal } = useConfirmModal();
|
||||||
const { jumpToIndex } = useNavigateHelper();
|
const { jumpToSignIn, jumpToIndex } = useNavigateHelper();
|
||||||
|
|
||||||
const authService = useService(AuthService);
|
const authService = useService(AuthService);
|
||||||
|
const { defaultServerService } = useServices({ DefaultServerService });
|
||||||
|
|
||||||
const signOut = useCallback(async () => {
|
const signOut = useCallback(async () => {
|
||||||
onConfirm?.()?.catch(console.error);
|
onConfirm?.()?.catch(console.error);
|
||||||
try {
|
try {
|
||||||
await authService.signOut();
|
await authService.signOut();
|
||||||
jumpToIndex();
|
if (
|
||||||
|
defaultServerService.server.config$.value.allowGuestDemoWorkspace !==
|
||||||
|
false
|
||||||
|
) {
|
||||||
|
jumpToIndex();
|
||||||
|
} else {
|
||||||
|
jumpToSignIn();
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
const error = UserFriendlyError.fromAny(err);
|
const error = UserFriendlyError.fromAny(err);
|
||||||
notify.error(error);
|
notify.error(error);
|
||||||
}
|
}
|
||||||
}, [authService, jumpToIndex, onConfirm]);
|
}, [authService, jumpToIndex, jumpToSignIn, defaultServerService, onConfirm]);
|
||||||
|
|
||||||
const getDefaultText = useCallback(
|
const getDefaultText = useCallback(
|
||||||
(key: SignOutConfirmModalI18NKeys) => {
|
(key: SignOutConfirmModalI18NKeys) => {
|
||||||
|
|||||||
+6
-5
@@ -1,5 +1,5 @@
|
|||||||
import { MenuItem } from '@affine/component/ui/menu';
|
import { MenuItem } from '@affine/component/ui/menu';
|
||||||
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
import { DefaultServerService } from '@affine/core/modules/cloud';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { ImportIcon, PlusIcon } from '@blocksuite/icons/rc';
|
import { ImportIcon, PlusIcon } from '@blocksuite/icons/rc';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
@@ -14,10 +14,11 @@ export const AddWorkspace = ({
|
|||||||
onNewWorkspace?: () => void;
|
onNewWorkspace?: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const featureFlagService = useService(FeatureFlagService);
|
const defaultServerService = useService(DefaultServerService);
|
||||||
const enableLocalWorkspace = useLiveData(
|
const allowGuestDemo = useLiveData(
|
||||||
featureFlagService.flags.enable_local_workspace.$
|
defaultServerService.server.config$.selector(c => c.allowGuestDemoWorkspace)
|
||||||
);
|
);
|
||||||
|
const guestDemoEnabled = allowGuestDemo !== false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -44,7 +45,7 @@ export const AddWorkspace = ({
|
|||||||
className={styles.ItemContainer}
|
className={styles.ItemContainer}
|
||||||
>
|
>
|
||||||
<div className={styles.ItemText}>
|
<div className={styles.ItemText}>
|
||||||
{enableLocalWorkspace
|
{guestDemoEnabled
|
||||||
? t['com.affine.workspaceList.addWorkspace.create']()
|
? t['com.affine.workspaceList.addWorkspace.create']()
|
||||||
: t['com.affine.workspaceList.addWorkspace.create-cloud']()}
|
: t['com.affine.workspaceList.addWorkspace.create-cloud']()}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+5
-5
@@ -1,8 +1,7 @@
|
|||||||
import { ScrollableContainer } from '@affine/component';
|
import { ScrollableContainer } from '@affine/component';
|
||||||
import { MenuItem } from '@affine/component/ui/menu';
|
import { MenuItem } from '@affine/component/ui/menu';
|
||||||
import { AuthService } from '@affine/core/modules/cloud';
|
import { AuthService, DefaultServerService } from '@affine/core/modules/cloud';
|
||||||
import { GlobalDialogService } from '@affine/core/modules/dialogs';
|
import { GlobalDialogService } from '@affine/core/modules/dialogs';
|
||||||
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
|
||||||
import { type WorkspaceMetadata } from '@affine/core/modules/workspace';
|
import { type WorkspaceMetadata } from '@affine/core/modules/workspace';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
@@ -66,7 +65,7 @@ export const UserWithWorkspaceList = ({
|
|||||||
}: UserWithWorkspaceListProps) => {
|
}: UserWithWorkspaceListProps) => {
|
||||||
const globalDialogService = useService(GlobalDialogService);
|
const globalDialogService = useService(GlobalDialogService);
|
||||||
const session = useLiveData(useService(AuthService).session.session$);
|
const session = useLiveData(useService(AuthService).session.session$);
|
||||||
const featureFlagService = useService(FeatureFlagService);
|
const defaultServerService = useService(DefaultServerService);
|
||||||
|
|
||||||
const isAuthenticated = session.status === 'authenticated';
|
const isAuthenticated = session.status === 'authenticated';
|
||||||
|
|
||||||
@@ -77,7 +76,8 @@ export const UserWithWorkspaceList = ({
|
|||||||
const onNewWorkspace = useCallback(() => {
|
const onNewWorkspace = useCallback(() => {
|
||||||
if (
|
if (
|
||||||
!isAuthenticated &&
|
!isAuthenticated &&
|
||||||
!featureFlagService.flags.enable_local_workspace.value
|
defaultServerService.server.config$.value.allowGuestDemoWorkspace ===
|
||||||
|
false
|
||||||
) {
|
) {
|
||||||
return openSignInModal();
|
return openSignInModal();
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,7 @@ export const UserWithWorkspaceList = ({
|
|||||||
onEventEnd?.();
|
onEventEnd?.();
|
||||||
}, [
|
}, [
|
||||||
globalDialogService,
|
globalDialogService,
|
||||||
featureFlagService,
|
defaultServerService,
|
||||||
isAuthenticated,
|
isAuthenticated,
|
||||||
onCreatedWorkspace,
|
onCreatedWorkspace,
|
||||||
onEventEnd,
|
onEventEnd,
|
||||||
|
|||||||
+2
-3
@@ -1,6 +1,7 @@
|
|||||||
import { IconButton, Menu, MenuItem } from '@affine/component';
|
import { IconButton, Menu, MenuItem } from '@affine/component';
|
||||||
import { Divider } from '@affine/component/ui/divider';
|
import { Divider } from '@affine/component/ui/divider';
|
||||||
import { useEnableCloud } from '@affine/core/components/hooks/affine/use-enable-cloud';
|
import { useEnableCloud } from '@affine/core/components/hooks/affine/use-enable-cloud';
|
||||||
|
import { useSignOut } from '@affine/core/components/hooks/affine/use-sign-out';
|
||||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
|
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
|
||||||
import type { AuthAccountInfo, Server } from '@affine/core/modules/cloud';
|
import type { AuthAccountInfo, Server } from '@affine/core/modules/cloud';
|
||||||
@@ -161,9 +162,7 @@ const CloudWorkSpaceList = ({
|
|||||||
workspaces,
|
workspaces,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const handleSignOut = useAsyncCallback(async () => {
|
const handleSignOut = useSignOut();
|
||||||
await authService.signOut();
|
|
||||||
}, [authService]);
|
|
||||||
|
|
||||||
const handleSignIn = useAsyncCallback(async () => {
|
const handleSignIn = useAsyncCallback(async () => {
|
||||||
globalDialogService.open('sign-in', {
|
globalDialogService.open('sign-in', {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { DefaultServerService } from '@affine/core/modules/cloud';
|
||||||
import { DesktopApiService } from '@affine/core/modules/desktop-api';
|
import { DesktopApiService } from '@affine/core/modules/desktop-api';
|
||||||
import { WorkspacesService } from '@affine/core/modules/workspace';
|
import { WorkspacesService } from '@affine/core/modules/workspace';
|
||||||
import {
|
import {
|
||||||
@@ -46,16 +47,23 @@ export const Component = ({
|
|||||||
const [navigating, setNavigating] = useState(true);
|
const [navigating, setNavigating] = useState(true);
|
||||||
const [creating, setCreating] = useState(false);
|
const [creating, setCreating] = useState(false);
|
||||||
const authService = useService(AuthService);
|
const authService = useService(AuthService);
|
||||||
|
const defaultServerService = useService(DefaultServerService);
|
||||||
|
|
||||||
const loggedIn = useLiveData(
|
const loggedIn = useLiveData(
|
||||||
authService.session.status$.map(s => s === 'authenticated')
|
authService.session.status$.map(s => s === 'authenticated')
|
||||||
);
|
);
|
||||||
|
const allowGuestDemo =
|
||||||
|
useLiveData(
|
||||||
|
defaultServerService.server.config$.selector(
|
||||||
|
c => c.allowGuestDemoWorkspace
|
||||||
|
)
|
||||||
|
) ?? true;
|
||||||
|
|
||||||
const workspacesService = useService(WorkspacesService);
|
const workspacesService = useService(WorkspacesService);
|
||||||
const list = useLiveData(workspacesService.list.workspaces$);
|
const list = useLiveData(workspacesService.list.workspaces$);
|
||||||
const listIsLoading = useLiveData(workspacesService.list.isRevalidating$);
|
const listIsLoading = useLiveData(workspacesService.list.isRevalidating$);
|
||||||
|
|
||||||
const { openPage, jumpToPage } = useNavigateHelper();
|
const { openPage, jumpToPage, jumpToSignIn } = useNavigateHelper();
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
|
|
||||||
const createOnceRef = useRef(false);
|
const createOnceRef = useRef(false);
|
||||||
@@ -84,6 +92,12 @@ export const Component = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!allowGuestDemo && !loggedIn) {
|
||||||
|
localStorage.removeItem('last_workspace_id');
|
||||||
|
jumpToSignIn();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// check is user logged in && has cloud workspace
|
// check is user logged in && has cloud workspace
|
||||||
if (searchParams.get('initCloud') === 'true') {
|
if (searchParams.get('initCloud') === 'true') {
|
||||||
if (loggedIn) {
|
if (loggedIn) {
|
||||||
@@ -111,10 +125,12 @@ export const Component = ({
|
|||||||
openPage(openWorkspace.id, defaultIndexRoute, RouteLogic.REPLACE);
|
openPage(openWorkspace.id, defaultIndexRoute, RouteLogic.REPLACE);
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
|
allowGuestDemo,
|
||||||
createCloudWorkspace,
|
createCloudWorkspace,
|
||||||
list,
|
list,
|
||||||
openPage,
|
openPage,
|
||||||
searchParams,
|
searchParams,
|
||||||
|
jumpToSignIn,
|
||||||
listIsLoading,
|
listIsLoading,
|
||||||
loggedIn,
|
loggedIn,
|
||||||
navigating,
|
navigating,
|
||||||
@@ -128,7 +144,9 @@ export const Component = ({
|
|||||||
}, [desktopApi]);
|
}, [desktopApi]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCreating(true);
|
if (listIsLoading || list.length > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
createFirstAppData(workspacesService)
|
createFirstAppData(workspacesService)
|
||||||
.then(createdWorkspace => {
|
.then(createdWorkspace => {
|
||||||
if (createdWorkspace) {
|
if (createdWorkspace) {
|
||||||
@@ -148,7 +166,15 @@ export const Component = ({
|
|||||||
.finally(() => {
|
.finally(() => {
|
||||||
setCreating(false);
|
setCreating(false);
|
||||||
});
|
});
|
||||||
}, [jumpToPage, openPage, workspacesService]);
|
}, [
|
||||||
|
jumpToPage,
|
||||||
|
jumpToSignIn,
|
||||||
|
openPage,
|
||||||
|
workspacesService,
|
||||||
|
loggedIn,
|
||||||
|
listIsLoading,
|
||||||
|
list,
|
||||||
|
]);
|
||||||
|
|
||||||
if (navigating || creating) {
|
if (navigating || creating) {
|
||||||
return fallback ?? <AppContainer fallback />;
|
return fallback ?? <AppContainer fallback />;
|
||||||
|
|||||||
@@ -83,7 +83,8 @@ const AcceptInvite = ({ inviteId: targetInviteId }: { inviteId: string }) => {
|
|||||||
|
|
||||||
const onSignOut = useAsyncCallback(async () => {
|
const onSignOut = useAsyncCallback(async () => {
|
||||||
await authService.signOut();
|
await authService.signOut();
|
||||||
}, [authService]);
|
navigateHelper.jumpToSignIn();
|
||||||
|
}, [authService, navigateHelper]);
|
||||||
|
|
||||||
if ((loading && !requestToJoinLoading) || inviteId !== targetInviteId) {
|
if ((loading && !requestToJoinLoading) || inviteId !== targetInviteId) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -228,7 +228,8 @@ const CloudWorkSpaceList = ({
|
|||||||
|
|
||||||
const handleSignOut = useAsyncCallback(async () => {
|
const handleSignOut = useAsyncCallback(async () => {
|
||||||
await authService.signOut();
|
await authService.signOut();
|
||||||
}, [authService]);
|
navigateHelper.jumpToSignIn();
|
||||||
|
}, [authService, navigateHelper]);
|
||||||
|
|
||||||
const handleSignIn = useAsyncCallback(async () => {
|
const handleSignIn = useAsyncCallback(async () => {
|
||||||
globalDialogService.open('sign-in', {
|
globalDialogService.open('sign-in', {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export const BUILD_IN_SERVERS: (ServerMetadata & { config: ServerConfig })[] =
|
|||||||
maxLength: 32,
|
maxLength: 32,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
allowGuestDemoWorkspace: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -56,6 +57,7 @@ export const BUILD_IN_SERVERS: (ServerMetadata & { config: ServerConfig })[] =
|
|||||||
maxLength: 32,
|
maxLength: 32,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
allowGuestDemoWorkspace: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -88,6 +90,7 @@ export const BUILD_IN_SERVERS: (ServerMetadata & { config: ServerConfig })[] =
|
|||||||
maxLength: 32,
|
maxLength: 32,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
allowGuestDemoWorkspace: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -120,6 +123,7 @@ export const BUILD_IN_SERVERS: (ServerMetadata & { config: ServerConfig })[] =
|
|||||||
maxLength: 32,
|
maxLength: 32,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
allowGuestDemoWorkspace: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -148,6 +152,7 @@ export const BUILD_IN_SERVERS: (ServerMetadata & { config: ServerConfig })[] =
|
|||||||
maxLength: 32,
|
maxLength: 32,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
allowGuestDemoWorkspace: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -178,6 +183,7 @@ export const BUILD_IN_SERVERS: (ServerMetadata & { config: ServerConfig })[] =
|
|||||||
maxLength: 32,
|
maxLength: 32,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
allowGuestDemoWorkspace: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ export class Server extends Entity<{
|
|||||||
credentialsRequirement: config.credentialsRequirement,
|
credentialsRequirement: config.credentialsRequirement,
|
||||||
features: config.features,
|
features: config.features,
|
||||||
oauthProviders: config.oauthProviders,
|
oauthProviders: config.oauthProviders,
|
||||||
|
allowGuestDemoWorkspace: config.allowGuestDemoWorkspace,
|
||||||
serverName: config.name,
|
serverName: config.name,
|
||||||
type: config.type,
|
type: config.type,
|
||||||
version: config.version,
|
version: config.version,
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ export class ServersService extends Service {
|
|||||||
credentialsRequirement: config.credentialsRequirement,
|
credentialsRequirement: config.credentialsRequirement,
|
||||||
features: config.features,
|
features: config.features,
|
||||||
oauthProviders: config.oauthProviders,
|
oauthProviders: config.oauthProviders,
|
||||||
|
allowGuestDemoWorkspace: config.allowGuestDemoWorkspace,
|
||||||
serverName: config.name,
|
serverName: config.name,
|
||||||
type: config.type,
|
type: config.type,
|
||||||
initialized: config.initialized,
|
initialized: config.initialized,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export interface ServerMetadata {
|
|||||||
export interface ServerConfig {
|
export interface ServerConfig {
|
||||||
serverName: string;
|
serverName: string;
|
||||||
features: ServerFeature[];
|
features: ServerFeature[];
|
||||||
|
allowGuestDemoWorkspace: boolean;
|
||||||
oauthProviders: OAuthProviderType[];
|
oauthProviders: OAuthProviderType[];
|
||||||
type: ServerDeploymentType;
|
type: ServerDeploymentType;
|
||||||
initialized?: boolean;
|
initialized?: boolean;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { FlagInfo } from './types';
|
import type { FlagInfo } from './types';
|
||||||
|
|
||||||
// const isNotStableBuild = BUILD_CONFIG.appBuildType !== 'stable';
|
// const isNotStableBuild = BUILD_CONFIG.appBuildType !== 'stable';
|
||||||
const isDesktopEnvironment = BUILD_CONFIG.isElectron;
|
|
||||||
const isCanaryBuild = BUILD_CONFIG.appBuildType === 'canary';
|
const isCanaryBuild = BUILD_CONFIG.appBuildType === 'canary';
|
||||||
const isMobile = BUILD_CONFIG.isMobileEdition;
|
const isMobile = BUILD_CONFIG.isMobileEdition;
|
||||||
|
|
||||||
@@ -149,15 +148,6 @@ export const AFFINE_FLAGS = {
|
|||||||
configurable: isCanaryBuild && !isMobile,
|
configurable: isCanaryBuild && !isMobile,
|
||||||
defaultState: isCanaryBuild,
|
defaultState: isCanaryBuild,
|
||||||
},
|
},
|
||||||
enable_local_workspace: {
|
|
||||||
category: 'affine',
|
|
||||||
displayName:
|
|
||||||
'com.affine.settings.workspace.experimental-features.enable-local-workspace.name',
|
|
||||||
description:
|
|
||||||
'com.affine.settings.workspace.experimental-features.enable-local-workspace.description',
|
|
||||||
configurable: isCanaryBuild,
|
|
||||||
defaultState: isDesktopEnvironment || isCanaryBuild,
|
|
||||||
},
|
|
||||||
enable_advanced_block_visibility: {
|
enable_advanced_block_visibility: {
|
||||||
category: 'blocksuite',
|
category: 'blocksuite',
|
||||||
bsFlag: 'enable_advanced_block_visibility',
|
bsFlag: 'enable_advanced_block_visibility',
|
||||||
|
|||||||
Reference in New Issue
Block a user