mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-16 01:26:37 +08:00
feat: redirect account click & OAuth to Calendar settings (#14693)
### PR Description * clicking a linked calendar account now switches settings to Workspace Integrations and opens the Calendar settings directly * calendar OAuth returns now land on Workspace Integrations with the Calendar settings opened instead of the homepage * Improves UX by reducing friction when managing calendar integrations https://www.loom.com/share/49fa5c448ce049659877beb42d7bd81a <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Calendar integration settings can now be opened automatically (including from OAuth redirects) and workspace settings support a scroll-to-anchor. * Integration account rows are now clickable for quick access to settings. * **Improvements** * Enhanced visual feedback with interactive hover and focus states for integration controls. * **Tests** * Added tests covering the OAuth redirect behavior and workspace settings scroll/open handling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: DarkSky <darksky2048@gmail.com>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* @vitest-environment happy-dom
|
||||
*/
|
||||
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import {
|
||||
buildWorkspaceSettingsPath,
|
||||
buildWorkspaceSettingsRedirectUri,
|
||||
} from '../use-navigate-helper';
|
||||
|
||||
describe('use-navigate-helper utilities', () => {
|
||||
test('buildWorkspaceSettingsPath includes tab and scroll anchor', () => {
|
||||
expect(
|
||||
buildWorkspaceSettingsPath('workspace-1', {
|
||||
tab: 'workspace:integrations',
|
||||
scrollAnchor: 'integration-calendar',
|
||||
})
|
||||
).toBe(
|
||||
'/workspace/workspace-1/settings?tab=workspace%3Aintegrations&scrollAnchor=integration-calendar'
|
||||
);
|
||||
});
|
||||
|
||||
test('buildWorkspaceSettingsRedirectUri builds a settings redirect from a workspace page', () => {
|
||||
expect(
|
||||
buildWorkspaceSettingsRedirectUri(
|
||||
'https://app.affine.pro/workspace/workspace-1/all',
|
||||
{
|
||||
tab: 'workspace:integrations',
|
||||
scrollAnchor: 'integration-calendar',
|
||||
}
|
||||
)
|
||||
).toBe(
|
||||
'https://app.affine.pro/workspace/workspace-1/settings?tab=workspace%3Aintegrations&scrollAnchor=integration-calendar'
|
||||
);
|
||||
});
|
||||
|
||||
test('buildWorkspaceSettingsRedirectUri preserves app subpaths before the workspace route', () => {
|
||||
expect(
|
||||
buildWorkspaceSettingsRedirectUri(
|
||||
'https://app.affine.pro/app/workspace/workspace-1/collection',
|
||||
{
|
||||
tab: 'workspace:integrations',
|
||||
scrollAnchor: 'integration-calendar',
|
||||
}
|
||||
)
|
||||
).toBe(
|
||||
'https://app.affine.pro/app/workspace/workspace-1/settings?tab=workspace%3Aintegrations&scrollAnchor=integration-calendar'
|
||||
);
|
||||
});
|
||||
|
||||
test('buildWorkspaceSettingsRedirectUri falls back to the current url when no workspace route is present', () => {
|
||||
expect(
|
||||
buildWorkspaceSettingsRedirectUri('https://app.affine.pro/sign-in', {
|
||||
tab: 'workspace:integrations',
|
||||
})
|
||||
).toBe('https://app.affine.pro/sign-in');
|
||||
});
|
||||
});
|
||||
@@ -17,6 +17,58 @@ export enum RouteLogic {
|
||||
PUSH = 'push',
|
||||
}
|
||||
|
||||
export type WorkspaceSettingsRouteOptions = {
|
||||
tab?: SettingTab;
|
||||
scrollAnchor?: string;
|
||||
};
|
||||
|
||||
export function buildWorkspaceSettingsPath(
|
||||
workspaceId: string,
|
||||
options?: WorkspaceSettingsRouteOptions
|
||||
) {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (options?.tab) {
|
||||
searchParams.set('tab', options.tab);
|
||||
}
|
||||
if (options?.scrollAnchor) {
|
||||
searchParams.set('scrollAnchor', options.scrollAnchor);
|
||||
}
|
||||
const query = searchParams.toString();
|
||||
return `/workspace/${workspaceId}/settings${query ? `?${query}` : ''}`;
|
||||
}
|
||||
|
||||
export function buildWorkspaceSettingsRedirectUri(
|
||||
currentHref: string,
|
||||
options?: WorkspaceSettingsRouteOptions
|
||||
): string {
|
||||
let currentUrl: URL;
|
||||
try {
|
||||
currentUrl = new URL(currentHref);
|
||||
} catch {
|
||||
return currentHref;
|
||||
}
|
||||
|
||||
const pathSegments = currentUrl.pathname.split('/').filter(Boolean);
|
||||
const workspaceSegmentIndex = pathSegments.indexOf('workspace');
|
||||
const workspaceId = pathSegments[workspaceSegmentIndex + 1];
|
||||
|
||||
if (workspaceSegmentIndex === -1 || !workspaceId) {
|
||||
return currentHref;
|
||||
}
|
||||
|
||||
const basePath = pathSegments.slice(0, workspaceSegmentIndex).join('/');
|
||||
const redirectUrl = new URL(
|
||||
buildWorkspaceSettingsPath(workspaceId, options),
|
||||
currentUrl.origin
|
||||
);
|
||||
|
||||
if (basePath) {
|
||||
redirectUrl.pathname = `/${basePath}${redirectUrl.pathname}`;
|
||||
}
|
||||
|
||||
return redirectUrl.toString();
|
||||
}
|
||||
|
||||
// TODO(@eyhn): add a name -> path helper in the results
|
||||
/**
|
||||
* Use this for over workbench navigate, for navigate in workbench, use `WorkbenchService`.
|
||||
@@ -213,18 +265,15 @@ export function useNavigateHelper() {
|
||||
const jumpToWorkspaceSettings = useCallback(
|
||||
(
|
||||
workspaceId: string,
|
||||
tab?: SettingTab,
|
||||
options?: WorkspaceSettingsRouteOptions | SettingTab,
|
||||
logic: RouteLogic = RouteLogic.PUSH
|
||||
) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (tab) {
|
||||
searchParams.set('tab', tab);
|
||||
}
|
||||
const resolvedOptions =
|
||||
typeof options === 'string' ? { tab: options } : options;
|
||||
|
||||
return navigate(
|
||||
`/workspace/${workspaceId}/settings?${searchParams.toString()}`,
|
||||
{
|
||||
replace: logic === RouteLogic.REPLACE,
|
||||
}
|
||||
buildWorkspaceSettingsPath(workspaceId, resolvedOptions),
|
||||
{ replace: logic === RouteLogic.REPLACE }
|
||||
);
|
||||
},
|
||||
[navigate]
|
||||
|
||||
Reference in New Issue
Block a user