fix: modified universal link (#7094)

This commit is contained in:
Kilu.He 2024-12-30 18:10:36 +08:00 committed by GitHub
parent dfe994b341
commit 3190eebf6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 104 additions and 86 deletions

View File

@ -1,25 +1 @@
{ {"applinks":{"apps":[],"details":[{"appIDs":["VHB67HRSZG.com.appflowy.appflowy.flutter"],"paths":["/download","/download/*"],"components":[{"/":"/download","comment":"Matches any URL whose path starts with /download"},{"/":"/download/*","comment":"Matches any URL whose path starts with /download/"}]}]},"webcredentials":{"apps":["VHB67HRSZG.com.appflowy.appflowy.flutter"]}}
"applinks": {
"apps": [],
"details": [
{
"appIDs": [
"VHB67HRSZG.com.appflowy.appflowy.flutter"
],
"paths": [
"*"
],
"components": [
{
"/": "/*"
}
]
}
]
},
"webcredentials": {
"apps": [
"VHB67HRSZG.com.appflowy.appflowy.flutter"
]
}
}

View File

@ -8,7 +8,8 @@
"package_name": "io.appflowy.appflowy", "package_name": "io.appflowy.appflowy",
"sha256_cert_fingerprints": [ "sha256_cert_fingerprints": [
"19:13:85:33:DB:B3:A2:FD:65:2F:61:D7:F2:35:95:79:FE:6E:CC:B5:AC:94:AA:02:9E:BE:E7:0E:02:6B:45:FF" "19:13:85:33:DB:B3:A2:FD:65:2F:61:D7:F2:35:95:79:FE:6E:CC:B5:AC:94:AA:02:9E:BE:E7:0E:02:6B:45:FF"
] ],
"path_prefix": "/download"
} }
} }
] ]

View File

@ -27,6 +27,7 @@ describe('Markdown editing', () => {
// Test `Bold` // Test `Bold`
cy.get('@editor').type('**bold'); cy.get('@editor').type('**bold');
cy.get('@editor').realPress(['*', '*']); cy.get('@editor').realPress(['*', '*']);
cy.wait(50);
expectedJson = [{ expectedJson = [{
type: 'paragraph', type: 'paragraph',
data: {}, data: {},

View File

@ -5,7 +5,7 @@ import React from 'react';
import { ReactComponent as Logo } from '@/assets/logo.svg'; import { ReactComponent as Logo } from '@/assets/logo.svg';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export function Login({ redirectTo }: { redirectTo: string }) { export function Login ({ redirectTo }: { redirectTo: string }) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
@ -27,11 +27,19 @@ export function Login({ redirectTo }: { redirectTo: string }) {
} }
> >
<span>{t('web.signInAgreement')} </span> <span>{t('web.signInAgreement')} </span>
<a href={'https://appflowy.io/terms'} target={'_blank'} className={'text-fill-default underline'}> <a
href={'https://appflowy.io/terms'}
target={'_blank'}
className={'text-fill-default underline'}
>
{t('web.termOfUse')} {t('web.termOfUse')}
</a>{' '} </a>{' '}
{t('web.and')}{' '} {t('web.and')}{' '}
<a href={'https://appflowy.io/privacy'} target={'_blank'} className={'text-fill-default underline'}> <a
href={'https://appflowy.io/privacy'}
target={'_blank'}
className={'text-fill-default underline'}
>
{t('web.privacyPolicy')} {t('web.privacyPolicy')}
</a> </a>
. .

View File

@ -20,35 +20,35 @@ const AppMain = withAppWrapper(() => {
<Routes> <Routes>
<Route <Route
path={'/:namespace/:publishName'} path={'/:namespace/:publishName'}
element={<PublishPage/>} element={<PublishPage />}
/> />
<Route <Route
path={'/login'} path={'/login'}
element={<Suspense><LoginPage/></Suspense>} element={<Suspense><LoginPage /></Suspense>}
/> />
<Route <Route
path={AUTH_CALLBACK_PATH} path={AUTH_CALLBACK_PATH}
element={<LoginAuth/>} element={<LoginAuth />}
/> />
<Route <Route
path="/404" path="/404"
element={<NotFound/>} element={<NotFound />}
/> />
<Route <Route
path="/after-payment" path="/after-payment"
element={<Suspense><AfterPaymentPage/></Suspense>} element={<Suspense><AfterPaymentPage /></Suspense>}
/> />
<Route <Route
path="/as-template" path="/as-template"
element={<Suspense><AsTemplatePage/></Suspense>} element={<Suspense><AsTemplatePage /></Suspense>}
/> />
<Route <Route
path="/accept-invitation" path="/accept-invitation"
element={<Suspense><AcceptInvitationPage/></Suspense>} element={<Suspense><AcceptInvitationPage /></Suspense>}
/> />
<Route <Route
path={'/import'} path={'/import'}
element={<Suspense><ImportPage/></Suspense>} element={<Suspense><ImportPage /></Suspense>}
/> />
<Route <Route
path="/" path="/"
@ -61,28 +61,23 @@ const AppMain = withAppWrapper(() => {
path="/app/*" path="/app/*"
element={ element={
<Suspense> <Suspense>
<AppRouter/> <AppRouter />
</Suspense> </Suspense>
} }
/> />
<Route <Route
path="*" path="*"
element={<NotFound/>} element={<NotFound />}
/> />
</Routes> </Routes>
); );
}); });
function App() { function App () {
const path = window.location.pathname;
if (path.startsWith('/.well-known')) {
return null;
}
return ( return (
<BrowserRouter> <BrowserRouter>
<AppMain/> <AppMain />
</BrowserRouter> </BrowserRouter>
); );
} }

View File

@ -5,7 +5,7 @@ import { QuickNote } from '@/application/types';
import { Button, CircularProgress } from '@mui/material'; import { Button, CircularProgress } from '@mui/material';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
function AddNote({ function AddNote ({
onEnterNote, onEnterNote,
onAdd, onAdd,
}: { }: {
@ -27,9 +27,10 @@ function AddNote({
<Button <Button
size={'small'} size={'small'}
color={'inherit'} color={'inherit'}
startIcon={loading ? <CircularProgress className={'w-4 h-4'}/> : <AddIcon className={'w-4 h-4'}/>} startIcon={loading ? <CircularProgress size={16} /> : <AddIcon className={'w-4 h-4'} />}
onClick={handleAdd} onClick={handleAdd}
className={'justify-start w-full'}> className={'justify-start w-full'}
>
{t('quickNote.addNote')} {t('quickNote.addNote')}
</Button> </Button>
</> </>

View File

@ -1,3 +1,4 @@
import CircularProgress from '@mui/material/CircularProgress';
import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import { IconButton, Tooltip, Zoom, Snackbar, Portal } from '@mui/material'; import { IconButton, Tooltip, Zoom, Snackbar, Portal } from '@mui/material';
import { ReactComponent as EditIcon } from '@/assets/edit.svg'; import { ReactComponent as EditIcon } from '@/assets/edit.svg';
@ -16,7 +17,7 @@ import { getPopoverPosition, setPopoverPosition } from '@/components/quick-note/
import Note from '@/components/quick-note/Note'; import Note from '@/components/quick-note/Note';
const PAPER_SIZE = [480, 396]; const PAPER_SIZE = [480, 396];
const Transition = React.forwardRef(function Transition( const Transition = React.forwardRef(function Transition (
props: TransitionProps & { props: TransitionProps & {
children: React.ReactElement; children: React.ReactElement;
}, },
@ -33,7 +34,7 @@ enum QuickNoteRoute {
LIST = 'list', LIST = 'list',
} }
export function QuickNote() { export function QuickNote () {
const { t } = useTranslation(); const { t } = useTranslation();
const modifier = useMemo(() => createHotKeyLabel(HOT_KEY_NAME.QUICK_NOTE), []); const modifier = useMemo(() => createHotKeyLabel(HOT_KEY_NAME.QUICK_NOTE), []);
const [open, setOpen] = React.useState(false); const [open, setOpen] = React.useState(false);
@ -197,6 +198,7 @@ export function QuickNote() {
useEffect(() => { useEffect(() => {
resetPosition(); resetPosition();
}, [resetPosition]); }, [resetPosition]);
const [loading, setLoading] = React.useState(false);
const buttonRef = useRef<HTMLButtonElement>(null); const buttonRef = useRef<HTMLButtonElement>(null);
const handleOpen = useCallback(async (forceCreate?: boolean) => { const handleOpen = useCallback(async (forceCreate?: boolean) => {
@ -217,7 +219,7 @@ export function QuickNote() {
} : prev); } : prev);
} }
await initNoteList(); void initNoteList();
if (route === QuickNoteRoute.LIST || forceCreate) { if (route === QuickNoteRoute.LIST || forceCreate) {
await handleAdd(); await handleAdd();
@ -232,7 +234,9 @@ export function QuickNote() {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
if (loading) return;
void (async () => { void (async () => {
setLoading(true);
try { try {
await handleOpen(true); await handleOpen(true);
// eslint-disable-next-line // eslint-disable-next-line
@ -240,6 +244,8 @@ export function QuickNote() {
console.error(e); console.error(e);
handleOpenToast(e.message); handleOpenToast(e.message);
} }
setLoading(false);
})(); })();
} else if (createHotkey(HOT_KEY_NAME.ESCAPE)(e)) { } else if (createHotkey(HOT_KEY_NAME.ESCAPE)(e)) {
handleClose(); handleClose();
@ -251,7 +257,7 @@ export function QuickNote() {
return () => { return () => {
document.removeEventListener('keydown', handleKeyDown); document.removeEventListener('keydown', handleKeyDown);
}; };
}, [handleOpen, handleOpenToast]); }, [handleOpen, handleOpenToast, loading]);
const handleMouseDown = (event: React.MouseEvent) => { const handleMouseDown = (event: React.MouseEvent) => {
if (!position) return; if (!position) return;
@ -393,7 +399,8 @@ export function QuickNote() {
expand={expand} expand={expand}
onToggleExpand={handleToggleExpand} onToggleExpand={handleToggleExpand}
onClose={handleClose} onClose={handleClose}
onBack={handleBackList}/> onBack={handleBackList}
/>
); );
} }
@ -423,26 +430,40 @@ export function QuickNote() {
return ( return (
<> <>
<Tooltip title={ <Tooltip
<> title={
<div>{t('quickNote.label')}</div> <>
<div className={'text-xs text-text-caption'}>{modifier}</div> <div>{t('quickNote.label')}</div>
</> <div className={'text-xs text-text-caption'}>{modifier}</div>
}> </>
}
>
<IconButton <IconButton
ref={buttonRef} ref={buttonRef}
size={'small'} size={'small'}
onClick={e => { onClick={async (e) => {
e.currentTarget.blur(); e.currentTarget.blur();
if (open) { if (open) {
handleClose(); handleClose();
return; return;
} }
void handleOpen(); try {
setLoading(true);
await handleOpen();
// eslint-disable-next-line
} catch (e: any) {
console.error(e);
handleOpenToast(e.message);
}
setLoading(false);
}} }}
disabled={loading}
> >
<EditIcon/> {loading ? <CircularProgress size={16} /> :
<EditIcon />}
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<Popover <Popover
@ -481,21 +502,24 @@ export function QuickNote() {
onClose={handleClose} onClose={handleClose}
keepMounted={true} keepMounted={true}
> >
<ToastContext.Provider value={{ <ToastContext.Provider
onOpen: handleOpenToast, value={{
onClose: () => { onOpen: handleOpenToast,
setToastMessage(''); onClose: () => {
setOpenToast(false); setToastMessage('');
}, setOpenToast(false);
open: openToast, },
}}> open: openToast,
}}
>
<div <div
onMouseDown={handleMouseDown} onMouseDown={handleMouseDown}
style={{ style={{
cursor: isDragging ? 'grabbing' : 'grab', cursor: isDragging ? 'grabbing' : 'grab',
}} }}
className={'bg-note-header py-2 px-5 flex items-center justify-between gap-5 h-[44px] w-full'}> className={'bg-note-header py-2 px-5 flex items-center justify-between gap-5 h-[44px] w-full'}
>
<div className={'flex-1 overflow-hidden w-full'}>{renderHeader()}</div> <div className={'flex-1 overflow-hidden w-full'}>{renderHeader()}</div>
</div> </div>
<div <div
@ -506,10 +530,12 @@ export function QuickNote() {
<> <>
<Note <Note
onAdd={handleAddedNote} onAdd={handleAddedNote}
onEnterNote={handleEnterNote} note={currentNote} onEnterNote={handleEnterNote}
note={currentNote}
onUpdateData={(data) => { onUpdateData={(data) => {
handleUpdateNodeData(currentNote.id, data); handleUpdateNodeData(currentNote.id, data);
}}/> }}
/>
</> : <NoteList </> : <NoteList
onAdd={handleAddedNote} onAdd={handleAddedNote}
onScroll={handleScrollList} onScroll={handleScrollList}

View File

@ -1,4 +1,4 @@
import { androidDownloadLink, desktopDownloadLink, iosDownloadLink, openAppFlowySchema } from '@/utils/url'; import { androidDownloadLink, desktopDownloadLink, openAppFlowySchema } from '@/utils/url';
type OS = 'ios' | 'android' | 'other'; type OS = 'ios' | 'android' | 'other';
@ -101,20 +101,30 @@ export const openAppOrDownload = (config: AppConfig): void => {
}; };
}; };
export function openOnly(schema?: string) { export function openOnly (schema?: string) {
return openAppOrDownload({ return openAppOrDownload({
appScheme: schema || openAppFlowySchema, appScheme: schema || openAppFlowySchema,
}); });
} }
export function openOrDownload(schema?: string) { export function openOrDownload (schema?: string) {
const os = getOS(); const os = getOS();
const downloadUrl = os === 'ios' ? iosDownloadLink : os === 'android' ? androidDownloadLink : desktopDownloadLink; if (os === 'ios' || os === 'android') {
const universalLink = 'https://appflowy.io/download';
const intentUrl = `intent://appflowy.io/download#Intent;` +
'scheme=https;' +
'package=io.appflowy.app;' +
`S.browser_fallback_url=${encodeURIComponent(androidDownloadLink)};` +
'end';
window.location.href = os === 'ios' ? universalLink : intentUrl;
return;
}
return openAppOrDownload({ return openAppOrDownload({
appScheme: schema || openAppFlowySchema, appScheme: schema || openAppFlowySchema,
downloadUrl, downloadUrl: desktopDownloadLink,
}); });
} }

View File

@ -2962,16 +2962,16 @@
"emails": "Email" "emails": "Email"
}, },
"quickNote": { "quickNote": {
"label": "Quick note", "label": "Quick Note",
"quickNotes": "Quick notes", "quickNotes": "Quick Notes",
"search": "Search quick notes", "search": "Search Quick Notes",
"collapseFullView": "Collapse full view", "collapseFullView": "Collapse full view",
"expandFullView": "Expand full view", "expandFullView": "Expand full view",
"createFailed": "Failed to create quick note", "createFailed": "Failed to create Quick Note",
"quickNotesEmpty": "No quick notes", "quickNotesEmpty": "No Quick Notes",
"emptyNote": "Empty note", "emptyNote": "Empty note",
"deleteNotePrompt": "The selected note will be deleted permanently. Are you sure you want to delete it?", "deleteNotePrompt": "The selected note will be deleted permanently. Are you sure you want to delete it?",
"addNote": "New note", "addNote": "New Note",
"noAdditionalText": "No additional text" "noAdditionalText": "No additional text"
}, },
"subscribe": { "subscribe": {