Skip to content

Commit

Permalink
fixes and refactories based on actual-server changes
Browse files Browse the repository at this point in the history
  • Loading branch information
lelemm committed Oct 8, 2024
1 parent 5cb9613 commit 97ebff9
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 94 deletions.
28 changes: 21 additions & 7 deletions packages/desktop-client/src/auth/ProtectedRoute.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useState, type ReactElement } from 'react';
import { useSelector } from 'react-redux';

import { send } from 'loot-core/platform/client/fetch';
import { type RemoteFile, type SyncedLocalFile } from 'loot-core/types/file';

import { View } from '../components/common/View';
import { useMetadataPref } from '../hooks/useMetadataPref';
Expand All @@ -22,19 +23,32 @@ export const ProtectedRoute = ({
const { hasPermission } = useAuth();
const [permissionGranted, setPermissionGranted] = useState(false);
const [cloudFileId] = useMetadataPref('cloudFileId');
const allFiles = useSelector(state => state.budgets.allFiles || []);
const remoteFiles = allFiles.filter(
f => f.state === 'remote' || f.state === 'synced' || f.state === 'detached',
) as (SyncedLocalFile | RemoteFile)[];
const currentFile = remoteFiles.find(f => f.cloudFileId === cloudFileId);
const userData = useSelector(state => state.user.data);

useEffect(() => {
const hasRequiredPermission = hasPermission(permission);
setPermissionGranted(hasRequiredPermission);

if (!hasRequiredPermission && validateOwner) {
send('check-file-access', cloudFileId).then(
({ granted }: { granted: boolean }) => {
setPermissionGranted(granted);
},
);
if (currentFile) {
setPermissionGranted(
currentFile.usersWithAccess.some(u => u.userId === userData?.userId),
);
}
}
}, [cloudFileId, permission, validateOwner, hasPermission]);
}, [
cloudFileId,
permission,
validateOwner,
hasPermission,
currentFile,
userData,
]);

return permissionGranted ? (
element
Expand Down
13 changes: 9 additions & 4 deletions packages/desktop-client/src/components/LoggedInUser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { Trans, useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom';

import { send } from 'loot-core/platform/client/fetch';
import { type State } from 'loot-core/src/client/state-types';
import { type RemoteFile, type SyncedLocalFile } from 'loot-core/types/file';

import { useAuth } from '../auth/AuthProvider';
import { Permissions } from '../auth/types';
Expand Down Expand Up @@ -46,6 +46,11 @@ export function LoggedInUser({
const { hasPermission } = useAuth();
const [isOwner, setIsOwner] = useState(false);
const multiuserEnabled = useMultiuserEnabled();
const allFiles = useSelector(state => state.budgets.allFiles || []);
const remoteFiles = allFiles.filter(
f => f.state === 'remote' || f.state === 'synced' || f.state === 'detached',
) as (SyncedLocalFile | RemoteFile)[];
const currentFile = remoteFiles.find(f => f.cloudFileId === cloudFileId);

useEffect(() => {
if (getUserData) {
Expand All @@ -54,9 +59,9 @@ export function LoggedInUser({
}, [getUserData]);

useEffect(() => {
if (cloudFileId) {
send('check-file-access', cloudFileId).then(
({ granted }: { granted: boolean }) => setIsOwner(granted),
if (cloudFileId && currentFile) {
setIsOwner(
currentFile.usersWithAccess.some(u => u.userId === userData?.userId),
);
}
}, [cloudFileId]);
Expand Down
110 changes: 29 additions & 81 deletions packages/desktop-client/src/components/manager/BudgetList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
loadBudget,
pushModal,
} from 'loot-core/client/actions';
import { send } from 'loot-core/platform/client/fetch';
import { isNonProductionEnvironment } from 'loot-core/src/shared/environment';
import {
type RemoteFile,
Expand All @@ -27,8 +26,6 @@ import {
type SyncableLocalFile,
type SyncedLocalFile,
} from 'loot-core/types/file';
import { type UserEntity } from 'loot-core/types/models';
import { type UserAccessEntity } from 'loot-core/types/models/userAccess';

import { useInitialMount } from '../../hooks/useInitialMount';
import { useMetadataPref } from '../../hooks/useMetadataPref';
Expand Down Expand Up @@ -146,16 +143,13 @@ function FileMenuButton({ onDelete }: { onDelete: () => void }) {

function FileState({
file,
users,
currentUserId,
isOpenID,
}: {
file: File;
users: UserEntity[];
currentUserId: string;
isOpenID: boolean;
}) {
const { t } = useTranslation();
const multiuserEnabled = useMultiuserEnabled();

let Icon;
let status;
Expand All @@ -172,7 +166,7 @@ function FileState({
case 'remote':
Icon = SvgCloudDownload;
status = t('Available for download');
ownerName = isOpenID ? getOwnerDisplayName() : '';
ownerName = multiuserEnabled ? getOwnerDisplayName() : '';
break;
case 'local':
Icon = SvgFileDouble;
Expand All @@ -186,15 +180,11 @@ function FileState({
default:
Icon = SvgCloudCheck;
status = t('Syncing');
ownerName = isOpenID ? getOwnerDisplayName() : '';
ownerName = multiuserEnabled ? getOwnerDisplayName() : '';
break;
}

const showOwnerContent =
isOpenID &&
ownerName !== null &&
!isLocalFile(file) &&
file.owner !== currentUserId;
const showOwnerContent = multiuserEnabled && file.owner !== currentUserId;

return (
<View style={{ width: '100%' }}>
Expand Down Expand Up @@ -245,12 +235,18 @@ function FileState({
);

function getOwnerDisplayName() {
const userFiltered = users.filter(item => item.id === file.owner);

if (userFiltered.length > 0) {
return userFiltered[0].displayName ?? userFiltered[0].userName;
if (
!(
file.state === 'remote' ||
file.state === 'synced' ||
file.state === 'detached'
)
) {
return '';
}
return null;

const userFound = file.usersWithAccess?.find(f => f.userId === file.owner);
return userFound?.displayName ?? userFound?.userName ?? 'unknown';
}
}

Expand All @@ -259,21 +255,16 @@ function FileItem({
quickSwitchMode,
onSelect,
onDelete,
users,
currentUserId,
usersPerFile,
isOpenID,
}: {
file: File;
quickSwitchMode: boolean;
onSelect: (file: File) => void;
onDelete: (file: File) => void;
users: UserEntity[];
currentUserId: string;
usersPerFile: Map<string, UserAccessEntity[]>;
isOpenID: boolean;
}) {
const { t } = useTranslation();
const multiuserEnabled = useMultiuserEnabled();

const selecting = useRef(false);

Expand Down Expand Up @@ -310,22 +301,16 @@ function FileItem({
<View style={{ alignItems: 'flex-start', width: '100%' }}>
<View style={{ flexDirection: 'row', width: '100%' }}>
<Text style={{ fontSize: 16, fontWeight: 700 }}>{file.name}</Text>
{isOpenID && (
{multiuserEnabled && (
<UserAccessForFile
fileId={(file as RemoteFile).cloudFileId}
ownerId={file.owner}
currentUserId={currentUserId}
usersPerFile={usersPerFile}
/>
)}
</View>

<FileState
file={file}
users={users}
currentUserId={currentUserId}
isOpenID={isOpenID}
/>
<FileState file={file} currentUserId={currentUserId} />
</View>

<View
Expand Down Expand Up @@ -355,19 +340,13 @@ function BudgetFiles({
quickSwitchMode,
onSelect,
onDelete,
users,
currentUserId,
usersPerFile,
isOpenID,
}: {
files: File[];
quickSwitchMode: boolean;
onSelect: (file: File) => void;
onDelete: (file: File) => void;
users: UserEntity[];
currentUserId: string;
usersPerFile: Map<string, UserAccessEntity[]>;
isOpenID: boolean;
}) {
return (
<View
Expand Down Expand Up @@ -396,13 +375,10 @@ function BudgetFiles({
<FileItem
key={isLocalFile(file) ? file.id : file.cloudFileId}
file={file}
users={users}
currentUserId={currentUserId}
usersPerFile={usersPerFile}
quickSwitchMode={quickSwitchMode}
onSelect={onSelect}
onDelete={onDelete}
isOpenID={isOpenID}
/>
))
)}
Expand Down Expand Up @@ -469,62 +445,32 @@ function BudgetListHeader({
export function BudgetList({ showHeader = true, quickSwitchMode = false }) {
const dispatch = useDispatch();
const allFiles = useSelector(state => state.budgets.allFiles || []);
const multiuserEnabled = useMultiuserEnabled();
const [id] = useMetadataPref('id');
const [users, setUsers] = useState<UserEntity[]>([]);
const [currentUserId, setCurrentUserId] = useState('');
const userData = useSelector(state => state.user.data);
const [usersPerFile, setUsersPerFile] = useState(
new Map<string, UserAccessEntity[]>(),
);
const multiuserEnabled = useMultiuserEnabled();

const fetchUsers = useCallback(async () => {
try {
const data: UserEntity[] = await send('users-get');
setUsers(data);
setCurrentUserId(userData?.userId ?? '');
} catch (error) {
console.error('Failed to fetch users:', error);
}
}, [userData?.userId]);

const fetchFileAccess = useCallback(async () => {
try {
const fileIds = allFiles
.filter(file => !isLocalFile(file))
.map(
file =>
(file as RemoteFile | SyncedLocalFile | SyncableLocalFile)
.cloudFileId,
);
const data: Map<string, UserAccessEntity[]> = await send(
'users-get-access',
fileIds,
);
setUsersPerFile(data);
} catch (error) {
console.error('Failed to fetch file access:', error);
}
}, [allFiles]);

useEffect(() => {
if (multiuserEnabled && !userData?.offline) {
fetchUsers();
}
}, [multiuserEnabled, userData?.offline, fetchUsers]);

useEffect(() => {
if (multiuserEnabled && !userData?.offline) {
fetchFileAccess();
}
}, [multiuserEnabled, userData?.offline, fetchFileAccess]);

// Remote files do not have the 'id' field
function isNonRemoteFile(
file: File,
): file is LocalFile | SyncableLocalFile | SyncedLocalFile {
return file.state !== 'remote';
}

const nonRemoteFiles = allFiles.filter(isNonRemoteFile);
const files = id ? nonRemoteFiles.filter(f => f.id !== id) : allFiles;

Expand Down Expand Up @@ -593,11 +539,8 @@ export function BudgetList({ showHeader = true, quickSwitchMode = false }) {
)}
<BudgetFiles
files={files}
users={users}
usersPerFile={usersPerFile}
currentUserId={currentUserId}
quickSwitchMode={quickSwitchMode}
isOpenID={multiuserEnabled}
onSelect={onSelect}
onDelete={file => dispatch(pushModal('delete-budget', { file }))}
/>
Expand Down Expand Up @@ -657,16 +600,21 @@ type UserAccessForFileProps = {
fileId: string;
currentUserId: string;
ownerId?: string;
usersPerFile: Map<string, UserAccessEntity[]>;
};

function UserAccessForFile({
fileId,
currentUserId,
ownerId,
usersPerFile,
}: UserAccessForFileProps) {
let usersAccess = usersPerFile?.has(fileId) ? usersPerFile.get(fileId) : [];
const allFiles = useSelector(state => state.budgets.allFiles || []);
const remoteFiles = allFiles.filter(
f => f.state === 'remote' || f.state === 'synced' || f.state === 'detached',
) as (SyncedLocalFile | RemoteFile)[];
const currentFile = remoteFiles.find(f => f.cloudFileId === fileId);
const multiuserEnabled = useMultiuserEnabled();

let usersAccess = currentFile?.usersWithAccess ?? [];
usersAccess = usersAccess?.filter(user => user.userId !== ownerId) ?? [];

const sortedUsersAccess = [...usersAccess].sort((a, b) => {
Expand All @@ -679,7 +627,7 @@ function UserAccessForFile({

return (
<View>
{usersAccess.length > 0 && (
{multiuserEnabled && usersAccess.length > 0 && (
<View
style={{
marginLeft: '5px',
Expand Down
3 changes: 3 additions & 0 deletions packages/loot-core/src/client/reducers/budgets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ function reconcileFiles(
hasKey: remote.hasKey,
state: 'synced',
owner: remote.owner,
usersWithAccess: remote.usersWithAccess,
};
} else {
return {
Expand All @@ -80,6 +81,7 @@ function reconcileFiles(
hasKey: remote.hasKey,
state: 'detached',
owner: remote.owner,
usersWithAccess: remote.usersWithAccess,
};
}
} else {
Expand Down Expand Up @@ -113,6 +115,7 @@ function reconcileFiles(
hasKey: f.hasKey,
state: 'remote',
owner: f.owner,
usersWithAccess: f.usersWithAccess,
};
}),
)
Expand Down
1 change: 1 addition & 0 deletions packages/loot-core/src/server/admin/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ app.method('access-delete-all', async function ({ fileId, ids }) {
const userToken = await asyncStorage.getItem('user-token');
if (userToken) {
try {
debugger;
const res = await del(
getServer().BASE_SERVER + `/admin/access?fileId=${fileId}`,
{
Expand Down
Loading

0 comments on commit 97ebff9

Please sign in to comment.