mirror of
https://github.com/docmost/docmost.git
synced 2026-05-15 05:04:06 +08:00
file attachments - WIP
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
import React from 'react';
|
||||
import { Avatar } from '@mantine/core';
|
||||
|
||||
interface UserAvatarProps extends React.ComponentProps<typeof Avatar> {
|
||||
interface UserAvatarProps {
|
||||
avatarUrl: string;
|
||||
name: string;
|
||||
color?: string;
|
||||
size?: string;
|
||||
radius?: string;
|
||||
style?: any;
|
||||
component?: any;
|
||||
}
|
||||
|
||||
export const UserAvatar = React.forwardRef<HTMLInputElement, UserAvatarProps>(
|
||||
|
||||
@@ -18,20 +18,21 @@ function RecentChanges() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
{data.map((page) => (
|
||||
{data
|
||||
.map((page) => (
|
||||
<div key={page.id}>
|
||||
<UnstyledButton component={Link} to={`/p/${page.id}`}
|
||||
className={classes.page} p="xs">
|
||||
<Group wrap="nowrap">
|
||||
|
||||
<Stack gap="xs" style={{ flex: 1 }}>
|
||||
<Text fw={500} size="sm">
|
||||
<Text fw={500} size="md" lineClamp={1}>
|
||||
{page.title || 'Untitled'}
|
||||
</Text>
|
||||
</Stack>
|
||||
|
||||
<Text c="dimmed" size="xs">
|
||||
{format(new Date(page.createdAt), 'PPP')}
|
||||
<Text c="dimmed" size="xs" fw={500}>
|
||||
{format(new Date(page.updatedAt), 'PP')}
|
||||
</Text>
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
|
||||
@@ -3,11 +3,15 @@ import AccountNameForm from '@/features/settings/account/settings/components/acc
|
||||
import ChangeEmail from '@/features/settings/account/settings/components/change-email';
|
||||
import ChangePassword from '@/features/settings/account/settings/components/change-password';
|
||||
import { Divider } from '@mantine/core';
|
||||
import AccountAvatar from '@/features/settings/account/settings/components/account-avatar';
|
||||
|
||||
export default function AccountSettings() {
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<AccountAvatar />
|
||||
|
||||
<AccountNameForm />
|
||||
|
||||
<Divider my="lg" />
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { focusAtom } from 'jotai-optics';
|
||||
import { currentUserAtom } from '@/features/user/atoms/current-user-atom';
|
||||
import { useState } from 'react';
|
||||
import { useAtom } from 'jotai';
|
||||
import { UserAvatar } from '@/components/ui/user-avatar';
|
||||
import { FileButton, Button, Text, Popover, Tooltip } from '@mantine/core';
|
||||
import { uploadAvatar } from '@/features/user/services/user-service';
|
||||
|
||||
const userAtom = focusAtom(currentUserAtom, (optic) => optic.prop('user'));
|
||||
|
||||
export default function AccountAvatar() {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [currentUser] = useAtom(currentUserAtom);
|
||||
const [, setUser] = useAtom(userAtom);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
|
||||
|
||||
const handleFileChange = async (selectedFile: File) => {
|
||||
if (!selectedFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (previewUrl) {
|
||||
URL.revokeObjectURL(previewUrl);
|
||||
}
|
||||
|
||||
setFile(selectedFile);
|
||||
setPreviewUrl(URL.createObjectURL(selectedFile));
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const upload = await uploadAvatar(selectedFile);
|
||||
console.log(upload);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<FileButton onChange={handleFileChange} accept="image/png,image/jpeg">
|
||||
{(props) => (
|
||||
<Tooltip label="Change photo"
|
||||
position="bottom"
|
||||
>
|
||||
<UserAvatar
|
||||
{...props}
|
||||
component="button"
|
||||
radius="xl"
|
||||
size="60px"
|
||||
avatarUrl={previewUrl || currentUser.user.avatarUrl}
|
||||
name={currentUser.user.name}
|
||||
style={{ cursor: 'pointer' }}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</FileButton>
|
||||
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -63,15 +63,15 @@ function ChangePasswordForm() {
|
||||
<PasswordInput
|
||||
label="Current password"
|
||||
name="current"
|
||||
placeholder="Enter your password"
|
||||
placeholder="Enter your current password"
|
||||
variant="filled"
|
||||
mb="md"
|
||||
{...form.getInputProps('password')}
|
||||
{...form.getInputProps('current')}
|
||||
/>
|
||||
|
||||
<PasswordInput
|
||||
label="New password"
|
||||
placeholder="Enter your password"
|
||||
placeholder="Enter your new password"
|
||||
variant="filled"
|
||||
mb="md"
|
||||
{...form.getInputProps('password')}
|
||||
|
||||
@@ -11,8 +11,20 @@ export async function getUserInfo(): Promise<ICurrentUserResponse> {
|
||||
return req.data as ICurrentUserResponse;
|
||||
}
|
||||
|
||||
export async function updateUser(data: Partial<IUser>) {
|
||||
export async function updateUser(data: Partial<IUser>): Promise<IUser> {
|
||||
const req = await api.post<IUser>('/user/update', data);
|
||||
|
||||
return req.data as IUser;
|
||||
}
|
||||
|
||||
export async function uploadAvatar(file: File) {
|
||||
const formData = new FormData();
|
||||
formData.append('avatar', file);
|
||||
const req = await api.post('/attachments/upload/avatar', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
}
|
||||
});
|
||||
return req.data;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user