Skip to content

Commit

Permalink
dev : user settings
Browse files Browse the repository at this point in the history
- ユーザーの設定の改善
  • Loading branch information
Liry24 committed Jan 26, 2025
1 parent 29c0ee3 commit f56a7e4
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 149 deletions.
2 changes: 1 addition & 1 deletion components/ui/header.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<header class="flex items-center justify-between w-full">
<div class="flex flex-wrap items-center gap-x-4 gap-y-2">
<NuxtLink to="/" class="pt-1">
<LogoLiria class="w-32 sm:w-48" aria-label="Avatio" />
<LogoLiria class="w-32 sm:w-48 -mt-1" aria-label="Avatio" />
</NuxtLink>
<BadgeHeader />
</div>
Expand Down
38 changes: 20 additions & 18 deletions components/ui/headerMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,25 +59,27 @@ onMounted(async () => {
</div>

<template v-if="route.path !== '/login'">
<UiTooltip v-if="user" :text="name ?? ''">
<NuxtLink
id="user"
tabindex="0"
:to="`/@${user?.id}`"
class="hidden sm:flex select-none rounded-full items-center outline outline-4 outline-transparent hover:outline-zinc-300 hover:dark:outline-zinc-600 transition-all ease-in-out duration-100"
>
<UiAvatar :url="avatar ?? ''" :alt="name ?? ''" />
</NuxtLink>
</UiTooltip>
<ClientOnly>
<UiTooltip v-if="user" :text="name ?? ''">
<NuxtLink
id="user"
tabindex="0"
:to="`/@${user?.id}`"
class="hidden sm:flex select-none rounded-full items-center outline outline-4 outline-transparent hover:outline-zinc-300 hover:dark:outline-zinc-600 transition-all ease-in-out duration-100"
>
<UiAvatar :url="avatar ?? ''" :alt="name ?? ''" />
</NuxtLink>
</UiTooltip>

<ButtonBase
v-else
id="login"
to="/login"
label="ログイン"
variant="flat"
class="hidden sm:block px-4 py-3 rounded-lg text-zinc-100 bg-zinc-500 dark:bg-zinc-600 hover:bg-zinc-600 hover:dark:bg-zinc-500"
/>
<ButtonBase
v-else
id="login"
to="/login"
label="ログイン"
variant="flat"
class="hidden sm:block px-4 py-3 rounded-lg text-zinc-100 bg-zinc-500 dark:bg-zinc-600 hover:bg-zinc-600 hover:dark:bg-zinc-500"
/>
</ClientOnly>
</template>

<PopupBase>
Expand Down
158 changes: 91 additions & 67 deletions components/userSetting/avatar.vue
Original file line number Diff line number Diff line change
@@ -1,62 +1,88 @@
<script lang="ts" setup>
interface Props {
initial: string;
}
const props = defineProps<Props>();
const avatar = ref<string>(props.initial);
const image = defineModel<File | null>({
default: null,
});
// interface Props {
// initial: string;
// }
// const props = defineProps<Props>();
// const avatar = ref<string>(props.initial);
const imagePreview = ref<string | ArrayBuffer | null>(null);
const client = await useSBClient();
const user = useSupabaseUser();
// const client = await useSBClient();
// const user = useSupabaseUser();
const loading = ref(false);
const changeAvatar = async () => {
const { open, onChange } = useFileDialog({
accept: 'image/png, image/jpeg, image/webp, image/avif, image/gif, image/svg, image/tiff',
multiple: false,
});
const faild = () => {
useAddToast(
'アバターの変更に失敗しました',
'画像の形式が非対応の可能性があります。'
);
loading.value = false;
};
open();
onChange(async (files) => {
if (!files || files.length === 0) throw new Error('No files selected');
const { open, onChange } = useFileDialog({
accept: 'image/png, image/jpeg, image/webp, image/avif, image/tiff',
multiple: false,
});
onChange((files) => {
if (files?.length) {
const file = files[0];
image.value = file;
const reader = new FileReader();
reader.onload = (e) => {
if (!e.target) return;
imagePreview.value = e.target.result;
};
reader.readAsDataURL(file);
} else {
image.value = null;
imagePreview.value = null;
}
});
// const changeAvatar = async () => {
// const { open, onChange } = useFileDialog({
// accept: 'image/png, image/jpeg, image/webp, image/avif, image/gif, image/svg, image/tiff',
// multiple: false,
// });
// const faild = () => {
// useAddToast(
// 'アバターの変更に失敗しました',
// '画像の形式が非対応の可能性があります。'
// );
// loading.value = false;
// };
// open();
// onChange(async (files) => {
// if (!files || files.length === 0) throw new Error('No files selected');
// const file = files[0];
// loading.value = true;
loading.value = true;
const uploaded = await usePutImage(file, {
resolution: 512,
size: 300,
prefix: 'avatar',
});
if (!uploaded) return faild();
const { data: legacy } = await client
.from('users')
.select('avatar')
.eq('id', user.value.id)
.maybeSingle();
const { error } = await client
.from('users')
.update({ avatar: uploaded.name } as never)
.eq('id', user.value.id);
if (error) return faild();
if (legacy) await useDeleteImage(legacy.avatar, { prefix: 'avatar' });
useAddToast('アバターを変更しました');
avatar.value = uploaded.name;
loading.value = false;
});
};
// const uploaded = await usePutImage(file, {
// resolution: 512,
// size: 300,
// prefix: 'avatar',
// });
// if (!uploaded) return faild();
// const { data: legacy } = await client
// .from('users')
// .select('avatar')
// .eq('id', user.value.id)
// .maybeSingle();
// const { error } = await client
// .from('users')
// .update({ avatar: uploaded.name } as never)
// .eq('id', user.value.id);
// if (error) return faild();
// if (legacy) await useDeleteImage(legacy.avatar, { prefix: 'avatar' });
// useAddToast('アバターを変更しました');
// avatar.value = uploaded.name;
// loading.value = false;
// });
// };
</script>

<template>
Expand All @@ -77,25 +103,23 @@ const changeAvatar = async () => {
</div>

<div
v-else-if="avatar && avatar.length"
class="flex items-center justify-center size-20 rounded-full flex-shrink-0 bg-zinc-200 dark:bg-zinc-500 relative"
v-else-if="imagePreview"
class="flex items-center justify-center size-20 rounded-full overflow-hidden flex-shrink-0 bg-zinc-200 dark:bg-zinc-500 relative"
>
<UiAvatar
:url="
avatar
? useGetImage(avatar, {
prefix: 'avatar',
})
: ''
"
alt="User avatar"
:icon-size="36"
class="size-20"
<NuxtImg
:src="imagePreview.toString()"
alt="アバター"
width="80"
height="80"
format="webp"
fit="cover"
loading="lazy"
class="flex-shrink-0"
/>
<button
type="button"
class="absolute inset-0 hover:bg-black/20 rounded-full"
@click="changeAvatar"
@click="open()"
/>
</div>

Expand All @@ -111,7 +135,7 @@ const changeAvatar = async () => {
<button
type="button"
class="absolute inset-0 hover:bg-black/20 rounded-full"
@click="changeAvatar"
@click="open()"
/>
</div>
</UiCard>
Expand Down
1 change: 0 additions & 1 deletion components/userSetting/links.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ const addLink = async () => {
const removeLink = async (link: string) => {
links.value = links.value.filter((i) => i !== link);
await useSaveLink(links.value);
};
const pasteFromClipboard = async () =>
Expand Down
53 changes: 0 additions & 53 deletions composables/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,56 +47,3 @@ export const useLoginWithTwitter = async () => {
useAddToast('ログインに失敗しました。');
}
};

export const useSaveUsername = async (username: string) => {
const client = await useSBClient();
const user = useSupabaseUser();

if (username === '') return useAddToast('ユーザー名を入力してください');

const { error } = await client
.from('users')
.update({ name: username } as never)
.eq('id', user.value.id);

if (error) {
useAddToast('ユーザー名の変更に失敗しました');
throw error;
}

useAddToast('ユーザー名を変更しました');
};

export const useSaveBio = async (bio: string) => {
const client = await useSBClient();
const user = useSupabaseUser();

const { error } = await client
.from('users')
.update({ bio: useLineBreak(bio) } as never)
.eq('id', user.value.id);

if (error) {
useAddToast('bioの変更に失敗しました');
throw error;
}

useAddToast('bioを変更しました');
};

export const useSaveLink = async (links: string[]) => {
const client = await useSBClient();
const user = useSupabaseUser();

const { error } = await client
.from('users')
.update({ links: links } as never)
.eq('id', user.value.id);

if (error) {
useAddToast('リンクの変更に失敗しました');
throw error;
}

useAddToast('リンクを変更しました');
};
14 changes: 10 additions & 4 deletions pages/search.vue
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,17 @@ watch(
:key="useId()"
:to="`/search?item=${i.id}`"
:aria-label="i.name"
class="w-32 p-4 gap-2 flex flex-col items-center rounded-lg border border-zinc-500 hover:bg-zinc-200 hover:dark:bg-zinc-600"
class="group relative size-32 rounded-lg overflow-hidden"
>
<div
class="absolute inset-0 flex items-center justify-center bg-black/60 opacity-0 group-hover:opacity-100 transition-all duration-200"
>
<span
class="p-1 text-sm text-center font-semibold text-white"
>
{{ useAvatarName(i.name) }}
</span>
</div>
<NuxtImg
v-slot="{ src, isLoaded }"
:src="i.thumbnail"
Expand All @@ -171,9 +180,6 @@ watch(
class="text-zinc-600 dark:text-zinc-300"
/>
</NuxtImg>
<p class="text-sm line-clamp-1 break-all">
{{ useAvatarName(i.name) }}
</p>
</NuxtLink>
</div>
</div>
Expand Down
37 changes: 32 additions & 5 deletions pages/settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,49 @@ const { data } = await client
.from('users')
.select('name, avatar, bio, links')
.eq('id', user.value.id)
.maybeSingle();
.maybeSingle<{
name: string;
avatar: string;
bio: string;
links: string[];
}>();
console.log(data);
const name = ref<string>(data?.name ?? '');
const avatar = ref<string>(data?.avatar ?? '');
const bio = ref<string>(data?.bio ?? '');
const links = ref<string[]>(data?.links ?? []);
const checkSame = () =>
name.value === data?.name &&
bio.value === data?.bio &&
links.value === data?.links;
const save = async () => {
await useSaveUsername(name.value);
await useSaveBio(bio.value);
await useSaveLink(links.value);
if (name.value === '') return useAddToast('ユーザー名を入力してください');
const { error } = await client
.from('users')
.update({
name: name.value,
bio: useLineBreak(bio.value),
links: links,
})
.eq('id', user.value.id);
if (error) return useAddToast('ユーザー情報の保存に失敗しました');
};
onMounted(() => {
useOGP({
title: 'ユーザー設定',
});
console.log({
name: name.value,
avatar: avatar.value,
bio: bio.value,
links: links.value,
});
});
</script>

Expand All @@ -39,7 +66,7 @@ onMounted(() => {
size="lg"
is="h1"
/>
<ButtonBase label="保存" @click="save" />
<ButtonBase :disabled="checkSame()" label="保存" @click="save" />
</div>
<UserSettingName v-model="name" />
<UserSettingAvatar :initial="data.avatar" />
Expand Down
Loading

0 comments on commit f56a7e4

Please sign in to comment.