Skip to content

Commit

Permalink
Add avatar update and delete
Browse files Browse the repository at this point in the history
  • Loading branch information
zobweyt committed Aug 23, 2024
1 parent e36f6b1 commit 1f0235d
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 9 deletions.
2 changes: 1 addition & 1 deletion frontend/src/lib/api/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ export interface components {
* File
* Format: binary
*/
file: string;
file: Blob;
};
/** Code */
Code: {
Expand Down
15 changes: 15 additions & 0 deletions frontend/src/lib/api/users/me/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { action } from "@solidjs/router";
import { $deleteCurrentUserAvatar, $updateCurrentUserAvatar } from "./server";

export const deleteCurrentUserAvatar = action(async () => {
"use server";

await $deleteCurrentUserAvatar();
});

export const updateCurrentUserAvatar = action(async (file: File) => {
"use server";

await $updateCurrentUserAvatar(file);
});

26 changes: 26 additions & 0 deletions frontend/src/lib/api/users/me/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { formDataSerializer, getClient } from "~/lib/api";

export const $deleteCurrentUserAvatar = async () => {
"use server";

const client = await getClient();

const { response } = await client.DELETE("/api/v1/users/me/avatar");

return response.status !== 404;
};

export const $updateCurrentUserAvatar = async (file: File) => {
"use server";

const client = await getClient();

const { response } = await client.PATCH("/api/v1/users/me/avatar", {
body: {
file: file,
},
bodySerializer: formDataSerializer,
});

return response.status;
};
51 changes: 43 additions & 8 deletions frontend/src/routes/(app)/(sidebar)/(authenticated)/settings.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { useSubmission } from "@solidjs/router";
import { useAction, useSubmission } from "@solidjs/router";
import { Show } from "solid-js";

import { Icon } from "solid-heroicons";
import { pencilSquare, userCircle } from "solid-heroicons/solid-mini";
import { pencilSquare, trash, userCircle } from "solid-heroicons/solid-mini";

import { Button, Heading, LocaleSwitcher, Separator, ThemeSwitcher, Title } from "~/components";
import { Button, Dropdown, Heading, LocaleSwitcher, Separator, ThemeSwitcher, Title } from "~/components";
import { formatResourceURL } from "~/lib/api/uploads";
import { unauthenticate, useCurrentUser } from "~/lib/auth";
import { useI18n } from "~/lib/i18n";
import { deleteCurrentUserAvatar, updateCurrentUserAvatar } from "~/lib/api/users/me/actions";

export default function Settings() {
const i18n = useI18n();
const currentUser = useCurrentUser();
const loggingOut = useSubmission(unauthenticate);
const updateAvatar = useAction(updateCurrentUserAvatar);

return (
<>
Expand All @@ -35,11 +37,44 @@ export default function Settings() {
</p>
</hgroup>
<div class="size-8">
<Show when={user().avatar_url} fallback={<Icon path={userCircle} />}>
{(avatar_url) => (
<img src={formatResourceURL(avatar_url())} alt="Profile picture" class="rounded-full" />
)}
</Show>
<Dropdown gutter={2} placement="bottom">
<Dropdown.Trigger>
<Show when={user().avatar_url} fallback={<Icon class="size-8" path={userCircle} />}>
{(avatar_url) => (
<img
src={formatResourceURL(avatar_url())}
alt={i18n.t.routes.settings.sections.personal.fields.avatar.heading()}
class="rounded-full"
/>
)}
</Show>
</Dropdown.Trigger>
<Dropdown.Content class="min-w-64">
<Dropdown.Item as={"label"} for="avatar" class="justify-start gap-2">
<input
type="file"
name="avatar"
id="avatar"
accept="image/*"
class="sr-only"
onChange={async (e) => await updateAvatar(e.currentTarget.files?.[0]!)}
/>
<Icon class="size-3.5" path={pencilSquare} />
Upload new avatar
</Dropdown.Item>
<Dropdown.Item
as={"form"}
method="post"
action={deleteCurrentUserAvatar}
disabled={user().avatar_url === null}
>
<button type="submit" class="flex size-full cursor-default items-center gap-2 text-red-600">
<Icon class="size-3.5" path={trash} />
Remove avatar
</button>
</Dropdown.Item>
</Dropdown.Content>
</Dropdown>
</div>
</div>

Expand Down

0 comments on commit 1f0235d

Please sign in to comment.