Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17,594 changes: 7,701 additions & 9,893 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

33 changes: 19 additions & 14 deletions src/components/_shared/notify/CustomSnackbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,45 @@ const CustomSnackbar = React.forwardRef<HTMLDivElement, CustomContentProps>((pro
const { closeSnackbar } = useSnackbar();

const icons = {
success: <TaskAltRounded className="w-6 h-6 text-green-500" />,
error: <HighlightOff className="w-6 h-6 text-red-500" />,
warning: <ErrorOutline className="w-6 h-6 text-yellow-500" />,
info: <PowerSettingsNew className="w-6 h-6 text-blue-500" />,
success: <TaskAltRounded className="w-5 h-5 text-green-500" />,
error: <HighlightOff className="w-5 h-5 text-red-500" />,
warning: <ErrorOutline className="w-5 h-5 text-yellow-500" />,
info: <PowerSettingsNew className="w-5 h-5 text-blue-500" />,
loading: null,
default: null,
};

const colors = {
success: 'bg-green-50 border-green-300',
error: 'bg-red-50',
warning: 'bg-yellow-50 border-yellow-300',
info: 'bg-blue-50 border-blue-300',
success: 'border-green-300 border bg-bg-body',
error: 'bg-bg-body border-red-300 border',
warning: 'bg-bg-body border-yellow-300 border',
info: 'bg-bg-body border-blue-300 border',
default: 'bg-bg-body border border-content-blue-400',
};

const [hovered, setHovered] = React.useState<boolean>(false);

return (
<SnackbarContent
ref={ref}
className={`${colors[variant]} rounded-lg shadow-lg`}
className={`${colors[variant]} rounded-lg shadow`}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<div className="flex items-center justify-between w-full p-4">
<div className="flex items-center justify-between w-full p-3">
<div className="flex-shrink-0">
{icons[variant]}
</div>
<div className="ml-3 flex-1">
<p className="text-sm font-medium">{message}</p>
</div>
<IconButton
className={'mx-2'}
{hovered && <IconButton
className={'mx-2 rounded-full bg-fill-list-hover hover:opacity-100 opacity-60'}
onClick={() => closeSnackbar(id)}
>
<CloseIcon className="h-5 w-5 text-text-caption" />
</IconButton>
<CloseIcon className="h-3 w-3 text-text-title" />
</IconButton>}

</div>
</SnackbarContent>
);
Expand Down
25 changes: 25 additions & 0 deletions src/components/app/app.hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
YjsEditorKey,
YSharedRoot,
} from '@/application/types';
import { notify } from '@/components/_shared/notify';
import { findAncestors, findView, findViewByLayout } from '@/components/_shared/outline/utils';
import RequestAccess from '@/components/app/landing-pages/RequestAccess';
import { AFConfigContext, useService } from '@/components/main/app.hooks';
Expand Down Expand Up @@ -62,6 +63,8 @@ export interface AppContextType {
updateSpace?: (payload: UpdateSpacePayload) => Promise<void>;
uploadFile?: (viewId: string, file: File, onProgress?: (n: number) => void) => Promise<string>;
getSubscriptions?: () => Promise<Subscription[]>;
publish?: (view: View, publishName?: string) => Promise<void>;
unpublish?: (viewId: string) => Promise<void>;
}

const USER_NO_ACCESS_CODE = [1024, 1012];
Expand Down Expand Up @@ -641,6 +644,24 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
}
}, [currentWorkspaceId, service]);

const publish = useCallback(async (view: View, publishName?: string) => {
if (!service || !currentWorkspaceId) return;
const isDatabase = [ViewLayout.Board, ViewLayout.Grid, ViewLayout.Calendar].includes(view.layout);
const viewId = view.view_id;

await service.publishView(currentWorkspaceId, viewId, {
publish_name: publishName,
visible_database_view_ids: isDatabase ? view.children?.map((v) => v.view_id) : undefined,
});
void loadOutline(currentWorkspaceId, false);
}, [currentWorkspaceId, loadOutline, service]);

const unpublish = useCallback(async (viewId: string) => {
if (!service || !currentWorkspaceId) return;
await service.unpublishView(currentWorkspaceId, viewId);
void loadOutline(currentWorkspaceId, false);
}, [currentWorkspaceId, loadOutline, service]);

return <AppContext.Provider
value={{
currentWorkspaceId,
Expand Down Expand Up @@ -679,6 +700,8 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
updateSpace,
uploadFile,
getSubscriptions,
publish,
unpublish,
}}
>
{requestAccessOpened ? <RequestAccess /> : children}
Expand Down Expand Up @@ -819,6 +842,8 @@ export function useAppHandlers () {
updateSpace: context.updateSpace,
uploadFile: context.uploadFile,
getSubscriptions: context.getSubscriptions,
publish: context.publish,
unpublish: context.unpublish,
};
}

Expand Down
26 changes: 12 additions & 14 deletions src/components/app/publish-manage/PublishManage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SubscriptionPlan, View, ViewLayout } from '@/application/types';
import { SubscriptionPlan, View } from '@/application/types';
import { notify } from '@/components/_shared/notify';
import { flattenViews } from '@/components/_shared/outline/utils';
import { useAppHandlers, useUserWorkspaceInfo } from '@/components/app/app.hooks';
Expand Down Expand Up @@ -129,36 +129,34 @@ export function PublishManage ({

}, [isOwner, service, t, workspaceId]);

const currentWorkspaceId = userWorkspaceInfo?.selectedWorkspace?.id;
const {
publish,
unpublish,
} = useAppHandlers();
const handlePublish = useCallback(async (view: View, publishName: string) => {
if (!service || !currentWorkspaceId) return;
const isDatabase = [ViewLayout.Board, ViewLayout.Grid, ViewLayout.Calendar].includes(view.layout);
const viewId = view.view_id;
if (!publish) return;

try {
await service.publishView(currentWorkspaceId, viewId, {
publish_name: publishName,
visible_database_view_ids: isDatabase ? view.children?.map((v) => v.view_id) : undefined,
});
await publish(view, publishName);
notify.success(t('publish.publishSuccessfully'));
// eslint-disable-next-line
} catch (e: any) {
notify.error(e.message);
}
}, [currentWorkspaceId, service, t]);
}, [publish, t]);

const handleUnpublish = useCallback(async (viewId: string) => {
if (!service || !currentWorkspaceId) return;
if (!unpublish) return;

try {
await service.unpublishView(currentWorkspaceId, viewId);
await unpublish(viewId);
void loadPublishPages();
notify.success(t('publish.unpublishSuccessfully'));
// eslint-disable-next-line
} catch (e: any) {
notify.error(e.message);
}
}, [currentWorkspaceId, loadPublishPages, service, t]);
}, [loadPublishPages, t, unpublish]);

const {
getSubscriptions,
Expand Down Expand Up @@ -252,7 +250,7 @@ export function PublishManage ({
if (!isOwner || activeSubscription === SubscriptionPlan.Free) {
return;
}

e.currentTarget.blur();
setUpdateOpen(true);
}}
Expand Down
14 changes: 11 additions & 3 deletions src/components/app/share/PublishLinkPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { CircularProgress, IconButton, InputBase, Tooltip } from '@mui/material'
import { ReactComponent as LinkIcon } from '@/assets/link.svg';
import { ReactComponent as DownIcon } from '@/assets/chevron_down.svg';
import { ReactComponent as CheckIcon } from '@/assets/check.svg';
import React from 'react';
import React, { useEffect } from 'react';
import { useTranslation } from 'react-i18next';

function PublishLinkPreview ({
Expand All @@ -15,13 +15,15 @@ function PublishLinkPreview ({
url,
isOwner,
isPublisher,
onClose,
}: {
publishInfo: { namespace: string, publishName: string };
onUnPublish: () => Promise<void>;
onPublish: (publishName?: string) => Promise<void>;
url: string;
isOwner: boolean;
isPublisher: boolean;
onClose?: () => void;
}) {
const [siteOpen, setSiteOpen] = React.useState<boolean>(false);
const [renameOpen, setRenameOpen] = React.useState<boolean>(false);
Expand All @@ -30,6 +32,10 @@ function PublishLinkPreview ({
const [focused, setFocused] = React.useState<boolean>(false);
const [loading, setLoading] = React.useState<boolean>(false);

useEffect(() => {
setPublishName(publishInfo.publishName);

}, [publishInfo.publishName]);
const handlePublish = async () => {
if (loading) return;
if (publishName === publishInfo.publishName) return;
Expand Down Expand Up @@ -62,6 +68,7 @@ function PublishLinkPreview ({
size={'small'}
onClick={() => {
setSiteOpen(true);
onClose?.();
}}
>
<DownIcon className={'w-4 h-4'} />
Expand Down Expand Up @@ -111,6 +118,7 @@ function PublishLinkPreview ({
}

setRenameOpen(true);
onClose?.();
}}
>
{loading ? <CircularProgress size={14} /> :
Expand Down Expand Up @@ -142,7 +150,7 @@ function PublishLinkPreview ({
onPublish={onPublish}
url={url}
/>}
{siteOpen && <NormalModal
<NormalModal
okButtonProps={{
className: 'hidden',
}}
Expand All @@ -168,7 +176,7 @@ function PublishLinkPreview ({
/>
</div>

</NormalModal>}
</NormalModal>
</div>

</>
Expand Down
33 changes: 15 additions & 18 deletions src/components/app/share/PublishPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { ViewLayout } from '@/application/types';
import { notify } from '@/components/_shared/notify';
import { useCurrentWorkspaceId } from '@/components/app/app.hooks';
import { useAppHandlers } from '@/components/app/app.hooks';
import { useLoadPublishInfo } from '@/components/app/share/publish.hooks';
import PublishLinkPreview from '@/components/app/share/PublishLinkPreview';
import { useService } from '@/components/main/app.hooks';
import { Button, CircularProgress, Typography } from '@mui/material';
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { ReactComponent as PublishIcon } from '@/assets/publish.svg';

function PublishPanel ({ viewId }: { viewId: string }) {
const currentWorkspaceId = useCurrentWorkspaceId();
function PublishPanel ({ viewId, onClose }: { viewId: string; onClose: () => void }) {
const { t } = useTranslation();
const {
publish,
unpublish,
} = useAppHandlers();
const {
url,
loadPublishInfo,
Expand All @@ -22,40 +23,35 @@ function PublishPanel ({ viewId }: { viewId: string }) {
isPublisher,
} = useLoadPublishInfo(viewId);

const service = useService();
const handlePublish = useCallback(async (publishName?: string) => {
if (!service || !currentWorkspaceId || !view) return;
const isDatabase = [ViewLayout.Board, ViewLayout.Grid, ViewLayout.Calendar].includes(view.layout);
if (!publish || !view) return;

try {
await service.publishView(currentWorkspaceId, viewId, {
publish_name: publishName,
visible_database_view_ids: isDatabase ? view.children?.map((v) => v.view_id) : undefined,
});
await loadPublishInfo();
await publish(view, publishName);
void loadPublishInfo();
notify.success(t('publish.publishSuccessfully'));
// eslint-disable-next-line
} catch (e: any) {
notify.error(e.message);
}
}, [currentWorkspaceId, loadPublishInfo, service, t, view, viewId]);
}, [loadPublishInfo, publish, t, view]);

const handleUnpublish = useCallback(async () => {
if (!service || !currentWorkspaceId || !view) return;
if (!view || !unpublish) return;
if (!isOwner && !isPublisher) {
notify.error(t('settings.sites.error.publishPermissionDenied'));
return;
}

try {
await service.unpublishView(currentWorkspaceId, viewId);
await unpublish(viewId);
await loadPublishInfo();
notify.success(t('publish.unpublishSuccessfully'));
// eslint-disable-next-line
} catch (e: any) {
notify.error(e.message);
}
}, [currentWorkspaceId, isOwner, isPublisher, loadPublishInfo, service, t, view, viewId]);
}, [isOwner, isPublisher, loadPublishInfo, t, unpublish, view, viewId]);

const renderPublished = useCallback(() => {
if (!publishInfo || !view) return null;
Expand All @@ -67,6 +63,7 @@ function PublishPanel ({ viewId }: { viewId: string }) {
onUnPublish={handleUnpublish}
isOwner={isOwner}
isPublisher={isPublisher}
onClose={onClose}
/>
<div className={'flex items-center gap-4 justify-end w-full'}>
<Button
Expand All @@ -86,7 +83,7 @@ function PublishPanel ({ viewId }: { viewId: string }) {
>{t('shareAction.visitSite')}</Button>
</div>
</div>;
}, [handlePublish, handleUnpublish, isOwner, isPublisher, publishInfo, t, url, view]);
}, [handlePublish, handleUnpublish, isOwner, isPublisher, onClose, publishInfo, t, url, view]);

const renderUnpublished = useCallback(() => {
return <Button
Expand Down
11 changes: 8 additions & 3 deletions src/components/app/share/ShareButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export function ShareButton ({ viewId }: { viewId: string }) {
variant={'contained'}
color={'primary'}
>{t('shareAction.buttonText')}</Button>
{opened && <Popover
<Popover
keepMounted
open={opened}
anchorEl={ref.current}
onClose={() => setOpened(false)}
Expand All @@ -41,9 +42,13 @@ export function ShareButton ({ viewId }: { viewId: string }) {
}}
>
<div className={'flex flex-col gap-2 w-fit p-2'}>
<ShareTabs viewId={viewId} />
<ShareTabs
opened={opened}
viewId={viewId}
onClose={() => setOpened(false)}
/>
</div>
</Popover>}
</Popover>
</>
);
}
Expand Down
Loading
Loading