mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-11-30 05:34:21 +03:00
feat(core): avoid popup window being blocked (#6451)
This commit is contained in:
parent
3e9e2ce93b
commit
6fa4b7da54
@ -4,6 +4,7 @@ import { registerAffineCommand } from '@toeverything/infra';
|
||||
import type { createStore } from 'jotai';
|
||||
|
||||
import { openSettingModalAtom } from '../atoms';
|
||||
import { popupWindow } from '../utils';
|
||||
|
||||
export function registerAffineHelpCommands({
|
||||
t,
|
||||
@ -20,7 +21,7 @@ export function registerAffineHelpCommands({
|
||||
icon: <NewIcon />,
|
||||
label: t['com.affine.cmdk.affine.whats-new'](),
|
||||
run() {
|
||||
window.open(runtimeConfig.changelogUrl, '_blank');
|
||||
popupWindow(runtimeConfig.changelogUrl);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
@ -4,6 +4,7 @@ import { Loading } from '@affine/component/ui/loading';
|
||||
import { AffineShapeIcon } from '@affine/core/components/page-list';
|
||||
import { useCredentialsRequirement } from '@affine/core/hooks/affine/use-server-config';
|
||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||
import { popupWindow } from '@affine/core/utils';
|
||||
import { SubscriptionPlan, type SubscriptionRecurring } from '@affine/graphql';
|
||||
import {
|
||||
changePasswordMutation,
|
||||
@ -48,7 +49,7 @@ const usePaymentRedirect = () => {
|
||||
successCallbackLink: null,
|
||||
},
|
||||
});
|
||||
window.open(checkoutUrl, '_self', 'norefferer');
|
||||
popupWindow(checkoutUrl);
|
||||
}, [recurring, plan, coupon, idempotencyKey, checkoutSubscription]);
|
||||
};
|
||||
|
||||
|
@ -11,7 +11,7 @@ import { useCallback } from 'react';
|
||||
|
||||
import { useAppSettingHelper } from '../../../../../hooks/affine/use-app-setting-helper';
|
||||
import { appIconMap, appNames } from '../../../../../pages/open-app';
|
||||
import { mixpanel } from '../../../../../utils';
|
||||
import { mixpanel, popupWindow } from '../../../../../utils';
|
||||
import { relatedLinks } from './config';
|
||||
import * as styles from './style.css';
|
||||
import { UpdateCheckSection } from './update-check-section';
|
||||
@ -99,7 +99,7 @@ export const AboutAffine = () => {
|
||||
desc={t['com.affine.aboutAFFiNE.changelog.description']()}
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
window.open(runtimeConfig.changelogUrl, '_blank');
|
||||
popupWindow(runtimeConfig.changelogUrl);
|
||||
}}
|
||||
>
|
||||
<ArrowRightSmallIcon />
|
||||
@ -143,7 +143,7 @@ export const AboutAffine = () => {
|
||||
<div
|
||||
className={styles.communityItem}
|
||||
onClick={() => {
|
||||
window.open(link, '_blank');
|
||||
popupWindow(link);
|
||||
}}
|
||||
key={title}
|
||||
>
|
||||
|
@ -31,7 +31,7 @@ import { useMutation } from '../../../../../hooks/use-mutation';
|
||||
import { useQuery } from '../../../../../hooks/use-query';
|
||||
import type { SubscriptionMutator } from '../../../../../hooks/use-subscription';
|
||||
import { useUserSubscription } from '../../../../../hooks/use-subscription';
|
||||
import { mixpanel } from '../../../../../utils';
|
||||
import { mixpanel, popupWindow } from '../../../../../utils';
|
||||
import { SWRErrorBoundary } from '../../../../pure/swr-error-bundary';
|
||||
import { CancelAction, ResumeAction } from '../plans/actions';
|
||||
import * as styles from './style.css';
|
||||
@ -262,7 +262,7 @@ const PaymentMethodUpdater = () => {
|
||||
const update = useAsyncCallback(async () => {
|
||||
await trigger(null, {
|
||||
onSuccess: data => {
|
||||
window.open(data.createCustomerPortal, '_blank', 'noopener noreferrer');
|
||||
popupWindow(data.createCustomerPortal);
|
||||
},
|
||||
});
|
||||
}, [trigger]);
|
||||
@ -361,7 +361,7 @@ const InvoiceLine = ({
|
||||
|
||||
const open = useCallback(() => {
|
||||
if (invoice.link) {
|
||||
window.open(invoice.link, '_blank', 'noopener noreferrer');
|
||||
popupWindow(invoice.link);
|
||||
}
|
||||
}, [invoice.link]);
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Button } from '@affine/component';
|
||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||
import { useMutation } from '@affine/core/hooks/use-mutation';
|
||||
import { popupWindow } from '@affine/core/utils';
|
||||
import { createCheckoutSessionMutation } from '@affine/graphql';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
@ -51,11 +52,7 @@ export const AISubscribe = ({
|
||||
},
|
||||
{
|
||||
onSuccess: data => {
|
||||
const newTab = window.open(
|
||||
data.createCheckoutSession,
|
||||
'_blank',
|
||||
'noopener noreferrer'
|
||||
);
|
||||
const newTab = popupWindow(data.createCheckoutSession);
|
||||
if (newTab) {
|
||||
newTabRef.current = newTab;
|
||||
newTab.addEventListener('close', onClose);
|
||||
|
@ -5,6 +5,7 @@ import type {
|
||||
Subscription,
|
||||
SubscriptionMutator,
|
||||
} from '@affine/core/hooks/use-subscription';
|
||||
import { popupWindow } from '@affine/core/utils';
|
||||
import type { SubscriptionRecurring } from '@affine/graphql';
|
||||
import {
|
||||
createCheckoutSessionMutation,
|
||||
@ -309,13 +310,7 @@ const Upgrade = ({
|
||||
},
|
||||
{
|
||||
onSuccess: data => {
|
||||
// FIXME: safari prevents from opening new tab by window api
|
||||
// TODO(@xp): what if electron?
|
||||
const newTab = window.open(
|
||||
data.createCheckoutSession,
|
||||
'_blank',
|
||||
'noopener noreferrer'
|
||||
);
|
||||
const newTab = popupWindow(data.createCheckoutSession);
|
||||
|
||||
if (newTab) {
|
||||
newTabRef.current = newTab;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Tooltip } from '@affine/component';
|
||||
import { popupWindow } from '@affine/core/utils';
|
||||
import { Unreachable } from '@affine/env/constant';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { CloseIcon, NewIcon, ResetIcon } from '@blocksuite/icons';
|
||||
@ -181,13 +182,12 @@ export function AppUpdaterButton({
|
||||
onDownloadUpdate();
|
||||
}
|
||||
} else {
|
||||
window.open(
|
||||
`https://github.com/toeverything/AFFiNE/releases/tag/v${updateAvailable.version}`,
|
||||
'_blank'
|
||||
popupWindow(
|
||||
`https://github.com/toeverything/AFFiNE/releases/tag/v${updateAvailable.version}`
|
||||
);
|
||||
}
|
||||
} else if (changelogUnread) {
|
||||
window.open(runtimeConfig.changelogUrl, '_blank');
|
||||
popupWindow(runtimeConfig.changelogUrl);
|
||||
onOpenChangelog();
|
||||
} else {
|
||||
throw new Unreachable();
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Tooltip } from '@affine/component/ui/tooltip';
|
||||
import { popupWindow } from '@affine/core/utils';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { CloseIcon, NewIcon } from '@blocksuite/icons';
|
||||
import { Doc, useLiveData, useServiceOptional } from '@toeverything/infra';
|
||||
@ -70,7 +71,7 @@ export const HelpIsland = () => {
|
||||
<StyledIconWrapper
|
||||
data-testid="right-bottom-change-log-icon"
|
||||
onClick={() => {
|
||||
window.open(runtimeConfig.changelogUrl, '_blank');
|
||||
popupWindow(runtimeConfig.changelogUrl);
|
||||
}}
|
||||
>
|
||||
<NewIcon />
|
||||
|
@ -7,7 +7,7 @@ import { atomWithObservable, atomWithStorage } from 'jotai/utils';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { mixpanel } from '../utils';
|
||||
import { mixpanel, popupWindow } from '../utils';
|
||||
import { useAsyncCallback } from './affine-async-hooks';
|
||||
|
||||
function rpcToObservable<
|
||||
@ -191,7 +191,7 @@ export const useAppUpdater = () => {
|
||||
mixpanel.track('Button', {
|
||||
resolve: 'OpenChangelog',
|
||||
});
|
||||
window.open(runtimeConfig.changelogUrl, '_blank');
|
||||
popupWindow(runtimeConfig.changelogUrl);
|
||||
await setChangelogUnread(true);
|
||||
}, [setChangelogUnread]);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
||||
import { popupWindow } from '@affine/core/utils';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import type { To } from 'history';
|
||||
import { useCallback } from 'react';
|
||||
@ -32,7 +33,7 @@ export const WorkbenchLink = ({
|
||||
typeof to === 'string'
|
||||
? to
|
||||
: `${to.pathname}${to.search}${to.hash}`;
|
||||
window.open(basename + href, '_blank');
|
||||
popupWindow(basename + href);
|
||||
}
|
||||
} else {
|
||||
workbench.open(to);
|
||||
|
43
packages/frontend/core/src/pages/redirect.tsx
Normal file
43
packages/frontend/core/src/pages/redirect.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import { type LoaderFunction, Navigate, useLoaderData } from 'react-router-dom';
|
||||
|
||||
const trustedDomain = [
|
||||
'stripe.com',
|
||||
'github.com',
|
||||
'twitter.com',
|
||||
'discord.gg',
|
||||
'youtube.com',
|
||||
't.me',
|
||||
'reddit.com',
|
||||
];
|
||||
|
||||
export const loader: LoaderFunction = async ({ request }) => {
|
||||
const url = new URL(request.url);
|
||||
const searchParams = url.searchParams;
|
||||
const redirectUri = searchParams.get('redirect_uri');
|
||||
|
||||
if (!redirectUri) {
|
||||
return { allow: false };
|
||||
}
|
||||
|
||||
const target = new URL(redirectUri);
|
||||
|
||||
if (
|
||||
trustedDomain.some(domain =>
|
||||
new RegExp(`.?${domain}$`).test(target.hostname)
|
||||
)
|
||||
) {
|
||||
location.href = redirectUri;
|
||||
}
|
||||
|
||||
return { allow: true };
|
||||
};
|
||||
|
||||
export const Component = () => {
|
||||
const { allow } = useLoaderData() as { allow: boolean };
|
||||
|
||||
if (allow) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <Navigate to="/404" />;
|
||||
};
|
@ -75,6 +75,10 @@ export const topLevelRoutes = [
|
||||
path: '/onboarding',
|
||||
lazy: () => import('./pages/onboarding'),
|
||||
},
|
||||
{
|
||||
path: '/redirect-proxy',
|
||||
lazy: () => import('./pages/redirect'),
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
lazy: () => import('./pages/404'),
|
||||
|
@ -2,5 +2,6 @@ export * from './create-emotion-cache';
|
||||
export * from './fractional-indexing';
|
||||
export * from './intl-formatter';
|
||||
export * from './mixpanel';
|
||||
export * from './popup';
|
||||
export * from './string2color';
|
||||
export * from './toast';
|
||||
|
6
packages/frontend/core/src/utils/popup.ts
Normal file
6
packages/frontend/core/src/utils/popup.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export function popupWindow(target: string) {
|
||||
const url = new URL(runtimeConfig.serverUrlPrefix + '/redirect-proxy');
|
||||
url.searchParams.set('redirect_uri', target);
|
||||
|
||||
return window.open(url, '_blank', `noreferrer noopener`);
|
||||
}
|
Loading…
Reference in New Issue
Block a user