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
195 changes: 195 additions & 0 deletions react/src/components/SharedFolderPermissionInfoModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import { filterNonNullItems } from '../helper';
import { useSuspendedBackendaiClient } from '../hooks';
import { useCurrentUserInfo } from '../hooks/backendai';
import { useTanMutation } from '../hooks/reactQueryAlias';
import { usePainKiller } from '../hooks/usePainKiller';
import UserUnionIcon from './BAIIcons/UserUnionIcon';
import BAIModal, { BAIModalProps } from './BAIModal';
import BAITable from './BAITable';
import Flex from './Flex';
import VFolderPermissionCell from './VFolderPermissionCell';
import {
SharedFolderPermissionInfoModalFragment$data,
SharedFolderPermissionInfoModalFragment$key,
} from './__generated__/SharedFolderPermissionInfoModalFragment.graphql';
import { UserOutlined } from '@ant-design/icons';
import {
Alert,
App,
Button,
Descriptions,
Popconfirm,
Tooltip,
Typography,
theme,
} from 'antd';
import graphql from 'babel-plugin-relay/macro';
import { LogOut } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { useFragment } from 'react-relay';

interface SharedFolderPermissionInfoModalProps extends BAIModalProps {
vfolderFrgmt: SharedFolderPermissionInfoModalFragment$key | null;
onRequestClose: (success?: boolean) => void;
}

type VFolder = NonNullable<SharedFolderPermissionInfoModalFragment$data>;

const SharedFolderPermissionInfoModal: React.FC<
SharedFolderPermissionInfoModalProps
> = ({ vfolderFrgmt, onRequestClose, ...modalProps }) => {
const { t } = useTranslation();
const { token } = theme.useToken();
const { message } = App.useApp();
const [currentUser] = useCurrentUserInfo();
const baiClient = useSuspendedBackendaiClient();
const painKiller = usePainKiller();

const vfolder = useFragment(
graphql`
fragment SharedFolderPermissionInfoModalFragment on VirtualFolderNode {
id
name
creator
ownership_type
user_email
permission

...VFolderPermissionCellFragment
}
`,
vfolderFrgmt,
);

const leaveFolder = useTanMutation({
mutationFn: ({ folderName }: { folderName: string }) => {
return baiClient.vfolder.leave_invited(folderName);
},
});

return (
<BAIModal
title={t('data.SharedFolderPermission')}
onCancel={() => onRequestClose()}
footer={null}
{...modalProps}
>
<Flex direction="column" align="stretch" gap="lg">
<Alert
showIcon
type="info"
message={
vfolder?.ownership_type === 'user'
? t('data.folders.SharedFolderAlertDesc')
: t('data.folders.ProjectFolderAlertDesc')
}
/>
<Descriptions column={2} bordered title={t('data.FolderInfo')}>
<Descriptions.Item label={t('data.folders.Name')}>
<Typography.Text copyable>{vfolder?.name}</Typography.Text>
</Descriptions.Item>
<Descriptions.Item label={t('data.folders.Type')}>
{vfolder?.ownership_type === 'user' ? (
<Flex gap={'xs'}>
<Typography.Text>{t('data.User')}</Typography.Text>
<UserOutlined style={{ color: token.colorTextTertiary }} />
</Flex>
) : (
<Flex gap={'xs'}>
<Typography.Text>{t('data.Project')}</Typography.Text>
<UserUnionIcon style={{ color: token.colorTextTertiary }} />
</Flex>
)}
</Descriptions.Item>
<Descriptions.Item label={t('data.folders.Owner')}>
{vfolder?.creator || vfolder?.user_email}
</Descriptions.Item>
</Descriptions>

{vfolder?.ownership_type === 'user' ? (
<Flex direction="column" align="stretch">
<Typography.Title
level={5}
style={{ marginTop: 0, marginBottom: token.marginMD }}
>
{t('data.folders.Permission')}
</Typography.Title>
<BAITable<VFolder>
bordered
pagination={false}
dataSource={filterNonNullItems([vfolder])}
columns={[
{
key: 'userName',
title: t('general.E-Mail'),
render: () => currentUser.email,
},
{
key: 'permissions',
title: t('data.folders.MountPermission'),
render: (perm: string, vfolder) => {
return <VFolderPermissionCell vfolderFrgmt={vfolder} />;
},
},
{
key: 'control',
title: t('data.folders.Control'),
render: (data) => (
<Flex align="stretch" justify="center">
<Popconfirm
title={t('data.invitation.LeaveSharedFolderDesc', {
folderName: data?.name,
})}
onConfirm={() => {
leaveFolder.mutate(
{
folderName: data?.name,
},
{
onSuccess: () => {
message.success(
t(
'data.invitation.SuccessfullyLeftSharedFolder',
),
);
onRequestClose(true);
},
onError: (err) => {
message.error(
painKiller.relieve(err?.message) ||
t('general.ErrorOccurred'),
);
onRequestClose();
},
},
);
}}
>
<Tooltip
title={t('data.invitation.LeaveSharedFolder')}
placement="right"
>
<Button
size="small"
type="text"
icon={<LogOut />}
style={{
color: token.colorError,
background: token.colorErrorBg,
}}
/>
</Tooltip>
</Popconfirm>
</Flex>
),
},
]}
/>
</Flex>
) : null}
</Flex>
</BAIModal>
);
};

export default SharedFolderPermissionInfoModal;
18 changes: 17 additions & 1 deletion react/src/components/VFolderNodes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import EditableVFolderName from './EditableVFolderName';
import Flex from './Flex';
import { useFolderExplorerOpener } from './FolderExplorerOpener';
import InviteFolderSettingModal from './InviteFolderSettingModal';
import SharedFolderPermissionInfoModal from './SharedFolderPermissionInfoModal';
import VFolderNodeIdenticon from './VFolderNodeIdenticon';
import VFolderPermissionCell from './VFolderPermissionCell';
import {
Expand Down Expand Up @@ -84,6 +85,8 @@ const VFolderNodes: React.FC<VFolderNodesProps> = ({

const [currentVFolder, setCurrentVFolder] =
useState<VFolderNodeInList | null>(null);
const [currentSharedVFolder, setCurrentSharedVFolder] =
useState<VFolderNodeInList | null>(null);

const vfolders = useFragment(
graphql`
Expand All @@ -99,6 +102,7 @@ const VFolderNodes: React.FC<VFolderNodesProps> = ({
...VFolderPermissionCellFragment
...EditableVFolderNameFragment
...VFolderNodeIdenticonFragment
...SharedFolderPermissionInfoModalFragment
}
`,
vfoldersFrgmt,
Expand Down Expand Up @@ -220,7 +224,9 @@ const VFolderNodes: React.FC<VFolderNodesProps> = ({
background: token.colorInfoBg,
}}
onClick={() => {
setInviteFolderId(toLocalId(vfolder?.id ?? null));
vfolder?.user === currentUser?.uuid
? setInviteFolderId(toLocalId(vfolder?.id ?? null))
: setCurrentSharedVFolder(vfolder);
}}
/>
</Tooltip>
Expand Down Expand Up @@ -470,6 +476,16 @@ const VFolderNodes: React.FC<VFolderNodesProps> = ({
vfolderId={inviteFolderId}
open={!!inviteFolderId}
/>
<SharedFolderPermissionInfoModal
vfolderFrgmt={currentSharedVFolder}
open={!!currentSharedVFolder}
onRequestClose={(success?: boolean) => {
setCurrentSharedVFolder(null);
if (success) {
onRequestChange?.();
}
}}
/>
</>
);
};
Expand Down
1 change: 1 addition & 0 deletions react/src/hooks/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export type BackendAIClient = {
user: string;
vfolder: string;
}): Promise<any>;
leave_invited(name: string | null): Promise<any>;
};
supports: (feature: string) => boolean;
[key: string]: any;
Expand Down
10 changes: 1 addition & 9 deletions react/src/pages/VFolderNodeListPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import BAITabs from '../components/BAITabs';
import DeleteVFolderModal from '../components/DeleteVFolderModal';
import Flex from '../components/Flex';
import FolderCreateModal from '../components/FolderCreateModal';
import InviteFolderSettingModal from '../components/InviteFolderSettingModal';
import QuotaPerStorageVolumePanelCard from '../components/QuotaPerStorageVolumePanelCard';
import RestoreVFolderModal from '../components/RestoreVFolderModal';
import StorageStatusPanelCard from '../components/StorageStatusPanelCard';
Expand Down Expand Up @@ -99,7 +98,6 @@ const VFolderNodeListPage: React.FC<VFolderNodeListPageProps> = ({
const [selectedFolderList, setSelectedFolderList] = useState<
Array<VFolderNodesType>
>([]);
const [inviteFolderId, setInviteFolderId] = useState<string | null>(null);
const [isOpenCreateModal, { toggle: toggleCreateModal }] = useToggle(false);
const [isOpenDeleteModal, { toggle: toggleDeleteModal }] = useToggle(false);
const [isOpenRestoreModal, { toggle: toggleRestoreModal }] = useToggle(false);
Expand Down Expand Up @@ -207,6 +205,7 @@ const VFolderNodeListPage: React.FC<VFolderNodeListPageProps> = ({
...EditableVFolderNameFragment
...RestoreVFolderModalFragment
...VFolderNodeIdenticonFragment
...SharedFolderPermissionInfoModalFragment
}
}
count
Expand Down Expand Up @@ -592,13 +591,6 @@ const VFolderNodeListPage: React.FC<VFolderNodeListPageProps> = ({
/>
</Flex>
</BAICard>
<InviteFolderSettingModal
onRequestClose={() => {
setInviteFolderId(null);
}}
vfolderId={inviteFolderId}
open={inviteFolderId !== null}
/>
<FolderCreateModal
open={isOpenCreateModal}
onRequestClose={(success) => {
Expand Down
10 changes: 9 additions & 1 deletion resources/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@
"ExistingFolderName": "Vorhandener Ordnername",
"FileAndFolderNameRequired": "Datei-/Ordnername ist erforderlich",
"FolderAlreadyExists": "Ein Ordner mit diesem Namen gibt es bereits",
"FolderInfo": "Forder Info",
"FolderNameRequired": "Ordnername ist erforderlich",
"FolderNameTooLong": "Geben Sie einen Ordnernamen mit weniger als 64 Zeichen ein",
"FolderToCopy": "Ordner zum Kopieren",
Expand Down Expand Up @@ -256,6 +257,7 @@
"ReadWrite": "Lesen und schreiben",
"SearchByName": "Suche mit Name",
"SelectStorageHost": "Speicherhost auswählen",
"SharedFolderPermission": "Erlaubnis für gemeinsame Ordner",
"StorageStatus": "Speicherstatus",
"Type": "Art",
"Update": "Aktualisieren",
Expand Down Expand Up @@ -374,6 +376,7 @@
"Owner": "Inhaber",
"Ownership": "Eigentum",
"Permission": "Genehmigung",
"ProjectFolderAlertDesc": "Dieser Ordner wird an alle Mitglieder des Projekts geteilt",
"Rename": "Umbenennen",
"Restore": "Wiederherstellen",
"RestoreDescription": "Möchten Sie \"{{ folderName }}\" -Fordner wiederherstellen?",
Expand All @@ -382,6 +385,7 @@
"SelectPermission": "Berechtigung auswählen",
"Serve": "Das Modell dient",
"ShareFolder": "Ordner teilen",
"SharedFolderAlertDesc": "Dies ist ein extern geteilter Ordner.",
"SharedUser": "Freigegebener Benutzer",
"SharedUserDesc": "Nur Benutzer, die die Einladung angenommen haben, werden in der Liste angezeigt.\n\nBenutzer, die noch einladend sind, werden nicht angezeigt.",
"Status": "Status",
Expand All @@ -396,11 +400,14 @@
"FolderSharingNotAvailableToUser": "Die gemeinsame Nutzung von Ordnern ist für den/die gewünschten Benutzer nicht verfügbar:",
"InvitationError": "Einladung fehlgeschlagen. Der Benutzer ist möglicherweise bereits eingeladen.",
"Invited": "Erfolgreich eingeladen",
"LeaveSharedFolder": "Lassen Sie den gemeinsam genutzten Ordner",
"LeaveSharedFolderDesc": "Möchten Sie den freigegebenen Ordner \"{{Ordnername}}\" verlassen?",
"NoOneWasInvited": "Niemand eingeladen, da Einladung bereits existiert",
"NoOneWasShared": "Fehler beim Freigeben des Gruppenordners",
"NoValidEmails": "Es wurden keine gültigen E-Mails eingegeben",
"Shared": "Gruppenordner erfolgreich freigegeben",
"SharingError": "Die Freigabe ist fehlgeschlagen. Der Benutzer ist möglicherweise bereits freigegeben oder gehört nicht zu dem entsprechenden Projekt."
"SharingError": "Die Freigabe ist fehlgeschlagen. Der Benutzer ist möglicherweise bereits freigegeben oder gehört nicht zu dem entsprechenden Projekt.",
"SuccessfullyLeftSharedFolder": "Erfolgreich den gemeinsam genutzten Ordner verlassen"
},
"modelStore": {
"AddedItems": "Artikel hinzugefügt",
Expand Down Expand Up @@ -603,6 +610,7 @@
"Disabled": "Behinderte",
"E-Mail": "Email",
"Enabled": "aktiviert",
"ErrorOccurred": "Etwas lief schief. \nBitte versuchen Sie es später erneut.",
"ExtendLoginSession": "Eine Anmeldesitzung verlängern",
"Folders": "Ordner",
"General": "Allgemein",
Expand Down
Loading