mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-15 00:56:26 +08:00
feat(core): add enable url preview to workspace settings (#8089)
This commit is contained in:
+2
@@ -17,6 +17,7 @@ import { ExportPanel } from './export';
|
||||
import { LabelsPanel } from './labels';
|
||||
import { MembersPanel } from './members';
|
||||
import { ProfilePanel } from './profile';
|
||||
import { SharingPanel } from './sharing';
|
||||
import type { WorkspaceSettingDetailProps } from './types';
|
||||
|
||||
export const WorkspaceSettingDetail = ({
|
||||
@@ -67,6 +68,7 @@ export const WorkspaceSettingDetail = ({
|
||||
<EnableCloudPanel />
|
||||
<MembersPanel />
|
||||
</SettingWrapper>
|
||||
<SharingPanel />
|
||||
{environment.isElectron && (
|
||||
<SettingWrapper title={t['Storage and Export']()}>
|
||||
<ExportPanel
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
import { Switch } from '@affine/component';
|
||||
import {
|
||||
SettingRow,
|
||||
SettingWrapper,
|
||||
} from '@affine/component/setting-components';
|
||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||
import { WorkspaceShareSettingService } from '@affine/core/modules/share-setting';
|
||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
|
||||
|
||||
export const SharingPanel = () => {
|
||||
const workspace = useService(WorkspaceService).workspace;
|
||||
if (workspace.flavour === WorkspaceFlavour.LOCAL) {
|
||||
return null;
|
||||
}
|
||||
return <Sharing />;
|
||||
};
|
||||
|
||||
export const Sharing = () => {
|
||||
const t = useI18n();
|
||||
const shareSetting = useService(WorkspaceShareSettingService).sharePreview;
|
||||
const enableUrlPreview = useLiveData(shareSetting.enableUrlPreview$);
|
||||
const loading = useLiveData(shareSetting.isLoading$);
|
||||
|
||||
const handleCheck = useAsyncCallback(
|
||||
async (checked: boolean) => {
|
||||
await shareSetting.setEnableUrlPreview(checked);
|
||||
},
|
||||
[shareSetting]
|
||||
);
|
||||
|
||||
return (
|
||||
<SettingWrapper title={t['com.affine.settings.workspace.sharing.title']()}>
|
||||
<SettingRow
|
||||
name={t['com.affine.settings.workspace.sharing.url-preview.title']()}
|
||||
desc={t[
|
||||
'com.affine.settings.workspace.sharing.url-preview.description'
|
||||
]()}
|
||||
>
|
||||
<Switch
|
||||
checked={enableUrlPreview || false}
|
||||
onChange={handleCheck}
|
||||
disabled={loading}
|
||||
/>
|
||||
</SettingRow>
|
||||
</SettingWrapper>
|
||||
);
|
||||
};
|
||||
@@ -19,6 +19,7 @@ import { configurePermissionsModule } from './permissions';
|
||||
import { configureWorkspacePropertiesModule } from './properties';
|
||||
import { configureQuickSearchModule } from './quicksearch';
|
||||
import { configureShareDocsModule } from './share-doc';
|
||||
import { configureShareSettingModule } from './share-setting';
|
||||
import { configureSystemFontFamilyModule } from './system-font-family';
|
||||
import { configureTagModule } from './tag';
|
||||
import { configureTelemetryModule } from './telemetry';
|
||||
@@ -34,6 +35,7 @@ export function configureCommonModules(framework: Framework) {
|
||||
configureQuotaModule(framework);
|
||||
configurePermissionsModule(framework);
|
||||
configureShareDocsModule(framework);
|
||||
configureShareSettingModule(framework);
|
||||
configureTelemetryModule(framework);
|
||||
configureFindInPageModule(framework);
|
||||
configurePeekViewModule(framework);
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import type { GetEnableUrlPreviewQuery } from '@affine/graphql';
|
||||
import type { WorkspaceService } from '@toeverything/infra';
|
||||
import {
|
||||
backoffRetry,
|
||||
catchErrorInto,
|
||||
effect,
|
||||
Entity,
|
||||
fromPromise,
|
||||
LiveData,
|
||||
mapInto,
|
||||
onComplete,
|
||||
onStart,
|
||||
} from '@toeverything/infra';
|
||||
import { exhaustMap } from 'rxjs';
|
||||
|
||||
import { isBackendError, isNetworkError } from '../../cloud';
|
||||
import type { WorkspaceShareSettingStore } from '../stores/share-setting';
|
||||
|
||||
type EnableUrlPreview =
|
||||
GetEnableUrlPreviewQuery['workspace']['enableUrlPreview'];
|
||||
|
||||
const logger = new DebugLogger('affine:workspace-permission');
|
||||
|
||||
export class WorkspaceShareSetting extends Entity {
|
||||
enableUrlPreview$ = new LiveData<EnableUrlPreview | null>(null);
|
||||
isLoading$ = new LiveData(false);
|
||||
error$ = new LiveData<any>(null);
|
||||
|
||||
constructor(
|
||||
private readonly workspaceService: WorkspaceService,
|
||||
private readonly store: WorkspaceShareSettingStore
|
||||
) {
|
||||
super();
|
||||
this.revalidate();
|
||||
}
|
||||
|
||||
revalidate = effect(
|
||||
exhaustMap(() => {
|
||||
return fromPromise(signal =>
|
||||
this.store.fetchWorkspaceEnableUrlPreview(
|
||||
this.workspaceService.workspace.id,
|
||||
signal
|
||||
)
|
||||
).pipe(
|
||||
backoffRetry({
|
||||
when: isNetworkError,
|
||||
count: Infinity,
|
||||
}),
|
||||
backoffRetry({
|
||||
when: isBackendError,
|
||||
count: 3,
|
||||
}),
|
||||
mapInto(this.enableUrlPreview$),
|
||||
catchErrorInto(this.error$, error => {
|
||||
logger.error('Failed to fetch enableUrlPreview', error);
|
||||
}),
|
||||
onStart(() => this.isLoading$.setValue(true)),
|
||||
onComplete(() => this.isLoading$.setValue(false))
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
async waitForRevalidation(signal?: AbortSignal) {
|
||||
this.revalidate();
|
||||
await this.isLoading$.waitFor(isLoading => !isLoading, signal);
|
||||
}
|
||||
|
||||
async setEnableUrlPreview(enableUrlPreview: EnableUrlPreview) {
|
||||
await this.store.updateWorkspaceEnableUrlPreview(
|
||||
this.workspaceService.workspace.id,
|
||||
enableUrlPreview
|
||||
);
|
||||
await this.waitForRevalidation();
|
||||
}
|
||||
|
||||
override dispose(): void {
|
||||
this.revalidate.unsubscribe();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
export { WorkspaceShareSettingService } from './services/share-setting';
|
||||
|
||||
import { GraphQLService } from '@affine/core/modules/cloud';
|
||||
import {
|
||||
type Framework,
|
||||
WorkspaceScope,
|
||||
WorkspaceService,
|
||||
} from '@toeverything/infra';
|
||||
|
||||
import { WorkspaceShareSetting } from './entities/share-setting';
|
||||
import { WorkspaceShareSettingService } from './services/share-setting';
|
||||
import { WorkspaceShareSettingStore } from './stores/share-setting';
|
||||
|
||||
export function configureShareSettingModule(framework: Framework) {
|
||||
framework
|
||||
.scope(WorkspaceScope)
|
||||
.service(WorkspaceShareSettingService)
|
||||
.store(WorkspaceShareSettingStore, [GraphQLService])
|
||||
.entity(WorkspaceShareSetting, [
|
||||
WorkspaceService,
|
||||
WorkspaceShareSettingStore,
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Service } from '@toeverything/infra';
|
||||
|
||||
import { WorkspaceShareSetting } from '../entities/share-setting';
|
||||
|
||||
export class WorkspaceShareSettingService extends Service {
|
||||
sharePreview = this.framework.createEntity(WorkspaceShareSetting);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { GraphQLService } from '@affine/core/modules/cloud';
|
||||
import {
|
||||
getEnableUrlPreviewQuery,
|
||||
setEnableUrlPreviewMutation,
|
||||
} from '@affine/graphql';
|
||||
import { Store } from '@toeverything/infra';
|
||||
|
||||
export class WorkspaceShareSettingStore extends Store {
|
||||
constructor(private readonly graphqlService: GraphQLService) {
|
||||
super();
|
||||
}
|
||||
|
||||
async fetchWorkspaceEnableUrlPreview(
|
||||
workspaceId: string,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
const data = await this.graphqlService.gql({
|
||||
query: getEnableUrlPreviewQuery,
|
||||
variables: {
|
||||
id: workspaceId,
|
||||
},
|
||||
context: {
|
||||
signal,
|
||||
},
|
||||
});
|
||||
return data.workspace.enableUrlPreview;
|
||||
}
|
||||
|
||||
async updateWorkspaceEnableUrlPreview(
|
||||
workspaceId: string,
|
||||
enableUrlPreview: boolean,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
await this.graphqlService.gql({
|
||||
query: setEnableUrlPreviewMutation,
|
||||
variables: {
|
||||
id: workspaceId,
|
||||
enableUrlPreview,
|
||||
},
|
||||
context: {
|
||||
signal,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1170,6 +1170,32 @@ mutation verifyEmail($token: String!) {
|
||||
}`,
|
||||
};
|
||||
|
||||
export const getEnableUrlPreviewQuery = {
|
||||
id: 'getEnableUrlPreviewQuery' as const,
|
||||
operationName: 'getEnableUrlPreview',
|
||||
definitionName: 'workspace',
|
||||
containsFile: false,
|
||||
query: `
|
||||
query getEnableUrlPreview($id: String!) {
|
||||
workspace(id: $id) {
|
||||
enableUrlPreview
|
||||
}
|
||||
}`,
|
||||
};
|
||||
|
||||
export const setEnableUrlPreviewMutation = {
|
||||
id: 'setEnableUrlPreviewMutation' as const,
|
||||
operationName: 'setEnableUrlPreview',
|
||||
definitionName: 'updateWorkspace',
|
||||
containsFile: false,
|
||||
query: `
|
||||
mutation setEnableUrlPreview($id: ID!, $enableUrlPreview: Boolean!) {
|
||||
updateWorkspace(input: {id: $id, enableUrlPreview: $enableUrlPreview}) {
|
||||
id
|
||||
}
|
||||
}`,
|
||||
};
|
||||
|
||||
export const enabledFeaturesQuery = {
|
||||
id: 'enabledFeaturesQuery' as const,
|
||||
operationName: 'enabledFeatures',
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
query getEnableUrlPreview($id: String!) {
|
||||
workspace(id: $id) {
|
||||
enableUrlPreview
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
mutation setEnableUrlPreview($id: ID!, $enableUrlPreview: Boolean!) {
|
||||
updateWorkspace(input: { id: $id, enableUrlPreview: $enableUrlPreview }) {
|
||||
id
|
||||
}
|
||||
}
|
||||
@@ -2325,6 +2325,25 @@ export type VerifyEmailMutation = {
|
||||
verifyEmail: boolean;
|
||||
};
|
||||
|
||||
export type GetEnableUrlPreviewQueryVariables = Exact<{
|
||||
id: Scalars['String']['input'];
|
||||
}>;
|
||||
|
||||
export type GetEnableUrlPreviewQuery = {
|
||||
__typename?: 'Query';
|
||||
workspace: { __typename?: 'WorkspaceType'; enableUrlPreview: boolean };
|
||||
};
|
||||
|
||||
export type SetEnableUrlPreviewMutationVariables = Exact<{
|
||||
id: Scalars['ID']['input'];
|
||||
enableUrlPreview: Scalars['Boolean']['input'];
|
||||
}>;
|
||||
|
||||
export type SetEnableUrlPreviewMutation = {
|
||||
__typename?: 'Mutation';
|
||||
updateWorkspace: { __typename?: 'WorkspaceType'; id: string };
|
||||
};
|
||||
|
||||
export type EnabledFeaturesQueryVariables = Exact<{
|
||||
id: Scalars['String']['input'];
|
||||
}>;
|
||||
@@ -2619,6 +2638,11 @@ export type Queries =
|
||||
variables: SubscriptionQueryVariables;
|
||||
response: SubscriptionQuery;
|
||||
}
|
||||
| {
|
||||
name: 'getEnableUrlPreviewQuery';
|
||||
variables: GetEnableUrlPreviewQueryVariables;
|
||||
response: GetEnableUrlPreviewQuery;
|
||||
}
|
||||
| {
|
||||
name: 'enabledFeaturesQuery';
|
||||
variables: EnabledFeaturesQueryVariables;
|
||||
@@ -2831,6 +2855,11 @@ export type Mutations =
|
||||
variables: VerifyEmailMutationVariables;
|
||||
response: VerifyEmailMutation;
|
||||
}
|
||||
| {
|
||||
name: 'setEnableUrlPreviewMutation';
|
||||
variables: SetEnableUrlPreviewMutationVariables;
|
||||
response: SetEnableUrlPreviewMutation;
|
||||
}
|
||||
| {
|
||||
name: 'setWorkspaceExperimentalFeatureMutation';
|
||||
variables: SetWorkspaceExperimentalFeatureMutationVariables;
|
||||
|
||||
@@ -1407,6 +1407,9 @@
|
||||
"com.affine.settings.workspace.sharing.url-preview.description": "Allow URL unfurling by Slack & other social apps, even if a doc is only accessible by workspace members.",
|
||||
"com.affine.settings.workspace.sharing.url-preview.title": "Always Enable URL Preview",
|
||||
"com.affine.settings.workspace.storage.tip": "Click to move storage location.",
|
||||
"com.affine.settings.workspace.sharing.title": "Sharing",
|
||||
"com.affine.settings.workspace.sharing.url-preview.title": "Always Enable URL Preview",
|
||||
"com.affine.settings.workspace.sharing.url-preview.description": "Allow URL unfurling by Slack & other social apps, even if a doc is only accessible by workspace members.",
|
||||
"com.affine.share-menu.EnableCloudDescription": "Sharing doc requires AFFiNE Cloud.",
|
||||
"com.affine.share-menu.ShareMode": "Share mode",
|
||||
"com.affine.share-menu.SharePage": "Share doc",
|
||||
|
||||
Reference in New Issue
Block a user