chore: migrate appflowy.io to appflowy.com (#29)

This commit is contained in:
Kilu.He
2025-02-05 12:34:39 +08:00
committed by GitHub
parent 7b13cf7d77
commit 34607bf863
15 changed files with 66 additions and 47 deletions

View File

@@ -9,7 +9,7 @@ import { fetch } from 'bun';
const distDir = path.join(__dirname, 'dist'); const distDir = path.join(__dirname, 'dist');
const indexPath = path.join(distDir, 'index.html'); const indexPath = path.join(distDir, 'index.html');
const baseURL = process.env.AF_BASE_URL as string; const baseURL = process.env.AF_BASE_URL as string;
const defaultSite = 'https://appflowy.io'; const defaultSite = 'https://appflowy.com';
const setOrUpdateMetaTag = ($: CheerioAPI, selector: string, attribute: string, content: string) => { const setOrUpdateMetaTag = ($: CheerioAPI, selector: string, attribute: string, content: string) => {
if ($(selector).length === 0) { if ($(selector).length === 0) {

View File

@@ -2991,7 +2991,7 @@
"second": "Unlimited pages & blocks", "second": "Unlimited pages & blocks",
"three": "5 GB storage", "three": "5 GB storage",
"four": "Intelligent search", "four": "Intelligent search",
"five": "20 AI responses", "five": "10 AI responses",
"six": "Mobile app", "six": "Mobile app",
"seven": "Real-time collaboration" "seven": "Real-time collaboration"
}, },

View File

@@ -14,7 +14,7 @@ export const AUTH_CALLBACK_PATH = '/auth/callback';
export const AUTH_CALLBACK_URL = `${window.location.origin}${AUTH_CALLBACK_PATH}`; export const AUTH_CALLBACK_URL = `${window.location.origin}${AUTH_CALLBACK_PATH}`;
export function withSignIn() { export function withSignIn() {
return function ( return function(
// eslint-disable-next-line // eslint-disable-next-line
_target: any, _target: any,
_propertyKey: string, _propertyKey: string,
@@ -23,14 +23,14 @@ export function withSignIn() {
const originalMethod = descriptor.value; const originalMethod = descriptor.value;
// eslint-disable-next-line // eslint-disable-next-line
descriptor.value = async function (args: { redirectTo: string }) { descriptor.value = async function(args: { redirectTo: string }) {
const redirectTo = args.redirectTo; const redirectTo = args.redirectTo;
saveRedirectTo(redirectTo); saveRedirectTo(redirectTo);
try { try {
await originalMethod.apply(this, [args]); await originalMethod.apply(this, [args]);
} catch (e) { } catch(e) {
console.error(e); console.error(e);
return Promise.reject(e); return Promise.reject(e);
} }
@@ -43,8 +43,15 @@ export function withSignIn() {
export function afterAuth() { export function afterAuth() {
const redirectTo = getRedirectTo(); const redirectTo = getRedirectTo();
if (redirectTo) { if(redirectTo) {
clearRedirectTo(); clearRedirectTo();
window.location.href = decodeURIComponent(redirectTo); const url = new URL(decodeURIComponent(redirectTo));
const pathname = url.pathname;
if(pathname === '/' || !pathname) {
url.pathname = '/app';
}
window.location.href = url.toString();
} }
} }

View File

@@ -20,7 +20,7 @@ function AppFlowyPower ({
<div <div
onClick={() => { onClick={() => {
window.open('https://appflowy.io', '_blank'); window.open('https://appflowy.com', '_blank');
}} }}
style={{ style={{
width, width,

View File

@@ -72,7 +72,7 @@ export default function Help () {
<Button <Button
component={'a'} component={'a'}
target="_blank" target="_blank"
href={'https://www.appflowy.io/what-is-new'} href={'https://www.appflowy.com/what-is-new'}
className={'justify-start'} className={'justify-start'}
color={'inherit'} color={'inherit'}
startIcon={<WhatsNewIcon />} startIcon={<WhatsNewIcon />}

View File

@@ -23,7 +23,7 @@ function MobileMore ({
label: t('template.label'), label: t('template.label'),
icon: <TemplateIcon />, icon: <TemplateIcon />,
onClick: () => { onClick: () => {
window.open('https://appflowy.io/templates', '_blank'); window.open('https://appflowy.com/templates', '_blank');
}, },
}, },
{ {

View File

@@ -6,7 +6,7 @@ import { useNavigate } from 'react-router-dom';
import { ReactComponent as TrashIcon } from '@/assets/trash.svg'; import { ReactComponent as TrashIcon } from '@/assets/trash.svg';
import { QuickNote } from '@/components/quick-note'; import { QuickNote } from '@/components/quick-note';
function SideBarBottom () { function SideBarBottom() {
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -22,7 +22,7 @@ function SideBarBottom () {
<IconButton <IconButton
size={'small'} size={'small'}
onClick={() => { onClick={() => {
window.open('https://appflowy.io/templates', '_blank'); window.open(`${window.location.origin}/templates`, '_blank');
}} }}
> >
<TemplateIcon /> <TemplateIcon />

View File

@@ -12,21 +12,21 @@ import { useTranslation } from 'react-i18next';
import { ReactComponent as DeleteIcon } from '@/assets/trash.svg'; import { ReactComponent as DeleteIcon } from '@/assets/trash.svg';
import { ReactComponent as EditIcon } from '@/assets/edit.svg'; import { ReactComponent as EditIcon } from '@/assets/edit.svg';
function TemplatePanel ({ viewId }: { viewId: string }) { function TemplatePanel({ viewId }: { viewId: string }) {
const view = useAppView(viewId); const view = useAppView(viewId);
const service = useService(); const service = useService();
const [loading, setLoading] = React.useState(false); const [loading, setLoading] = React.useState(false);
const [deleteModalOpen, setDeleteModalOpen] = React.useState(false); const [deleteModalOpen, setDeleteModalOpen] = React.useState(false);
const [template, setTemplateInfo] = React.useState<Template>(); const [template, setTemplateInfo] = React.useState<Template>();
const loadTemplateInfo = useCallback(async () => { const loadTemplateInfo = useCallback(async() => {
if (!service || !view?.view_id) return; if(!service || !view?.view_id) return;
setLoading(true); setLoading(true);
try { try {
const res = await service.getTemplateById(view?.view_id); const res = await service.getTemplateById(view?.view_id);
setTemplateInfo(res); setTemplateInfo(res);
// eslint-disable-next-line // eslint-disable-next-line
} catch (e: any) { } catch(e: any) {
// do nothing // do nothing
} finally { } finally {
setLoading(false); setLoading(false);
@@ -38,9 +38,9 @@ function TemplatePanel ({ viewId }: { viewId: string }) {
} = useLoadPublishInfo(viewId); } = useLoadPublishInfo(viewId);
const url = useMemo(() => { const url = useMemo(() => {
const origin = import.meta.env.AF_BASE_URL?.includes('test') ? 'https://test.appflowy.io' : 'https://appflowy.io'; const templateUrl = `${window.location.origin}/templates`;
return template ? `${origin}/templates/${slugify(template.categories[0].name)}/${template.view_id}` : ''; return template ? `${templateUrl}/${slugify(template.categories[0].name)}/${template.view_id}` : '';
}, [template]); }, [template]);
useEffect(() => { useEffect(() => {

View File

@@ -15,7 +15,7 @@ import './template.scss';
import { slugify } from '@/components/as-template/utils'; import { slugify } from '@/components/as-template/utils';
import { ReactComponent as WebsiteIcon } from '@/assets/website.svg'; import { ReactComponent as WebsiteIcon } from '@/assets/website.svg';
function AsTemplate ({ function AsTemplate({
viewName, viewName,
viewUrl, viewUrl,
viewId, viewId,
@@ -37,8 +37,8 @@ function AsTemplate ({
loading, loading,
} = useLoadTemplate(viewId); } = useLoadTemplate(viewId);
const handleSubmit = useCallback(async (data: AsTemplateFormValue) => { const handleSubmit = useCallback(async(data: AsTemplateFormValue) => {
if (!service || !selectedCreatorId || selectedCategoryIds.length === 0) return; if(!service || !selectedCreatorId || selectedCategoryIds.length === 0) return;
const formData: UploadTemplatePayload = { const formData: UploadTemplatePayload = {
...data, ...data,
view_id: viewId, view_id: viewId,
@@ -50,7 +50,7 @@ function AsTemplate ({
}; };
try { try {
if (template) { if(template) {
await service?.updateTemplate(template.view_id, formData); await service?.updateTemplate(template.view_id, formData);
} else { } else {
await service?.createTemplate(formData); await service?.createTemplate(formData);
@@ -59,7 +59,7 @@ function AsTemplate ({
await loadTemplate(); await loadTemplate();
notify.success('Template saved successfully'); notify.success('Template saved successfully');
} catch (error) { } catch(error) {
// eslint-disable-next-line // eslint-disable-next-line
// @ts-ignore // @ts-ignore
notify.error(error.toString()); notify.error(error.toString());
@@ -73,7 +73,7 @@ function AsTemplate ({
}, [loadTemplate]); }, [loadTemplate]);
useEffect(() => { useEffect(() => {
if (!template) return; if(!template) return;
setSelectedCategoryIds(template.categories.map((category) => category.id)); setSelectedCategoryIds(template.categories.map((category) => category.id));
setSelectedCreatorId(template.creator.id); setSelectedCreatorId(template.creator.id);
setIsNewTemplate(template.is_new_template); setIsNewTemplate(template.is_new_template);
@@ -81,7 +81,7 @@ function AsTemplate ({
}, [template]); }, [template]);
const defaultValue = useMemo(() => { const defaultValue = useMemo(() => {
if (!template) return { if(!template) return {
name: viewName, name: viewName,
description: '', description: '',
about: '', about: '',
@@ -104,10 +104,11 @@ function AsTemplate ({
startIcon={<WebsiteIcon />} startIcon={<WebsiteIcon />}
variant={'text'} variant={'text'}
onClick={() => { onClick={() => {
const origin = import.meta.env.AF_BASE_URL?.includes('test') ? 'https://test.appflowy.io' : 'https://appflowy.io'; const templateUrl = `${window.location.origin}/templates`;
window.open(`${origin}/templates/${slugify(template.categories[0].name)}/${template.view_id}`); window.open(`${templateUrl}/${slugify(template.categories[0].name)}/${template.view_id}`);
}} color={'primary'} }}
color={'primary'}
>{t('template.viewTemplate')}</Button>} >{t('template.viewTemplate')}</Button>}
<div className={'flex items-center gap-2'}> <div className={'flex items-center gap-2'}>
{template && <Button {template && <Button
@@ -124,7 +125,9 @@ function AsTemplate ({
<Button <Button
onClick={() => { onClick={() => {
submitRef.current?.click(); submitRef.current?.click();
}} variant={'contained'} color={'primary'} }}
variant={'contained'}
color={'primary'}
> >
{t('button.save')} {t('button.save')}
</Button> </Button>
@@ -133,11 +136,15 @@ function AsTemplate ({
</div> </div>
<div className={'flex-1 flex gap-20 overflow-hidden'}> <div className={'flex-1 flex gap-20 overflow-hidden'}>
<Paper className={'w-full h-full flex-1 flex justify-center overflow-hidden'}> <Paper className={'w-full h-full flex-1 flex justify-center overflow-hidden'}>
<AFScroller className={'w-full h-full flex justify-center'} overflowXHidden> <AFScroller
className={'w-full h-full flex justify-center'}
overflowXHidden
>
{loading ? {loading ?
<CircularProgress /> : <CircularProgress /> :
<AsTemplateForm <AsTemplateForm
defaultValues={defaultValue} viewUrl={viewUrl} defaultValues={defaultValue}
viewUrl={viewUrl}
onSubmit={handleSubmit} onSubmit={handleSubmit}
ref={submitRef} ref={submitRef}
defaultRelatedTemplates={template?.related_templates} defaultRelatedTemplates={template?.related_templates}
@@ -146,8 +153,14 @@ function AsTemplate ({
</AFScroller> </AFScroller>
</Paper> </Paper>
<div className={'w-[25%] flex flex-col gap-4'}> <div className={'w-[25%] flex flex-col gap-4'}>
<Categories value={selectedCategoryIds} onChange={setSelectedCategoryIds} /> <Categories
<Creator value={selectedCreatorId} onChange={setSelectedCreatorId} /> value={selectedCategoryIds}
onChange={setSelectedCategoryIds}
/>
<Creator
value={selectedCreatorId}
onChange={setSelectedCreatorId}
/>
<div className={'flex gap-2 items-center'}> <div className={'flex gap-2 items-center'}>
<InputLabel>{t('template.isNewTemplate')}</InputLabel> <InputLabel>{t('template.isNewTemplate')}</InputLabel>
<Switch <Switch

View File

@@ -37,10 +37,9 @@ export const withPasted = (editor: ReactEditor) => {
if (isUrl) { if (isUrl) {
const isAppFlowyLinkUrl = isURL(text, { const isAppFlowyLinkUrl = isURL(text, {
host_whitelist: ['localhost', 'appflowy.com', 'test.appflowy.com', 'beta.appflowy.com'], host_whitelist: [window.location.hostname],
}); });
console.log('isAppFlowyLinkUrl', isAppFlowyLinkUrl);
if (isAppFlowyLinkUrl) { if (isAppFlowyLinkUrl) {
const url = new URL(text); const url = new URL(text);
const blockId = url.searchParams.get('blockId'); const blockId = url.searchParams.get('blockId');

View File

@@ -35,7 +35,7 @@ const NotFound = () => {
<div className={'flex items-center px-2 max-sm:flex-col mt-4 w-full gap-4 justify-between'}> <div className={'flex items-center px-2 max-sm:flex-col mt-4 w-full gap-4 justify-between'}>
<Button <Button
component={Link} component={Link}
to="https://appflowy.io/download" to="https://appflowy.com/download"
variant="contained" variant="contained"
color="primary" color="primary"
className={'flex-1 py-3 px-4 max-sm:w-full rounded-[8px] max-md:text-base text-[20px] font-medium'} className={'flex-1 py-3 px-4 max-sm:w-full rounded-[8px] max-md:text-base text-[20px] font-medium'}

View File

@@ -28,7 +28,7 @@ export function Login ({ redirectTo }: { redirectTo: string }) {
> >
<span>{t('web.signInAgreement')} </span> <span>{t('web.signInAgreement')} </span>
<a <a
href={'https://appflowy.io/terms'} href={'https://appflowy.com/terms'}
target={'_blank'} target={'_blank'}
className={'text-fill-default underline'} className={'text-fill-default underline'}
> >
@@ -36,7 +36,7 @@ export function Login ({ redirectTo }: { redirectTo: string }) {
</a>{' '} </a>{' '}
{t('web.and')}{' '} {t('web.and')}{' '}
<a <a
href={'https://appflowy.io/privacy'} href={'https://appflowy.com/privacy'}
target={'_blank'} target={'_blank'}
className={'text-fill-default underline'} className={'text-fill-default underline'}
> >

View File

@@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { ReactComponent as ErrorIcon } from '@/assets/error.svg'; import { ReactComponent as ErrorIcon } from '@/assets/error.svg';
function LoginAuth () { function LoginAuth() {
const service = useContext(AFConfigContext)?.service; const service = useContext(AFConfigContext)?.service;
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [modalOpened, setModalOpened] = useState(false); const [modalOpened, setModalOpened] = useState(false);
@@ -16,13 +16,13 @@ function LoginAuth () {
const openLoginModal = useContext(AFConfigContext)?.openLoginModal; const openLoginModal = useContext(AFConfigContext)?.openLoginModal;
useEffect(() => { useEffect(() => {
void (async () => { void (async() => {
setLoading(true); setLoading(true);
setError(null); setError(null);
try { try {
await service?.loginAuth(window.location.href); await service?.loginAuth(window.location.href);
// eslint-disable-next-line // eslint-disable-next-line
} catch (e: any) { } catch(e: any) {
setError(e.message); setError(e.message);
setModalOpened(true); setModalOpened(true);
} finally { } finally {
@@ -51,7 +51,7 @@ function LoginAuth () {
closable={false} closable={false}
cancelText={t('button.backToHome')} cancelText={t('button.backToHome')}
onOk={() => { onOk={() => {
openLoginModal?.(getRedirectTo() || window.location.origin); openLoginModal?.(getRedirectTo() || `${window.location.origin}/app`);
}} }}
okText={t('button.tryAgain')} okText={t('button.tryAgain')}
title={<div className={'text-left font-bold flex gap-2 items-center'}> title={<div className={'text-left font-bold flex gap-2 items-center'}>

View File

@@ -112,8 +112,8 @@ export function openOrDownload (schema?: string) {
const os = getOS(); const os = getOS();
if (os === 'ios' || os === 'android') { if (os === 'ios' || os === 'android') {
const universalLink = 'https://appflowy.io/download'; const universalLink = 'https://appflowy.com/download';
const intentUrl = `intent://appflowy.io/download#Intent;` + const intentUrl = `intent://appflowy.com/download#Intent;` +
'scheme=https;' + 'scheme=https;' +
'package=io.appflowy.app;' + 'package=io.appflowy.app;' +
`S.browser_fallback_url=${encodeURIComponent(androidDownloadLink)};` + `S.browser_fallback_url=${encodeURIComponent(androidDownloadLink)};` +

View File

@@ -2,14 +2,14 @@ import isURL from 'validator/lib/isURL';
import isIP from 'validator/lib/isIP'; import isIP from 'validator/lib/isIP';
import isFQDN from 'validator/lib/isFQDN'; import isFQDN from 'validator/lib/isFQDN';
export const downloadPage = 'https://appflowy.io/download'; export const downloadPage = 'https://appflowy.com/download';
export const openAppFlowySchema = 'appflowy-flutter://'; export const openAppFlowySchema = 'appflowy-flutter://';
export const iosDownloadLink = 'https://apps.apple.com/app/appflowy/id6457261352'; export const iosDownloadLink = 'https://apps.apple.com/app/appflowy/id6457261352';
export const androidDownloadLink = 'https://play.google.com/store/apps/details?id=io.appflowy.appflowy'; export const androidDownloadLink = 'https://play.google.com/store/apps/details?id=io.appflowy.appflowy';
export const desktopDownloadLink = 'https://appflowy.io/download/#pop'; export const desktopDownloadLink = 'https://appflowy.com/download/#pop';
export function isValidUrl(input: string) { export function isValidUrl(input: string) {
return isURL(input, { require_protocol: true, require_host: false }); return isURL(input, { require_protocol: true, require_host: false });