drop/server/components/UserHeader/FriendsWidgetPanel.vue

145 lines
4.5 KiB
Vue
Raw Permalink Normal View History

<template>
<PanelWidget class="flex-col gap-y-2">
<div class="border-b border-zinc-700 pb-3 p-2">
<div
class="-ml-4 -mt-2 flex flex-wrap items-center justify-between sm:flex-nowrap"
>
<div class="ml-4 mt-2">
<h3 class="text-base font-semibold text-zinc-100 text-sm">
{{ $t("community.friends.title") }}
</h3>
</div>
<div class="ml-4 mt-2 shrink-0">
<NuxtLink
to="/community/friends"
type="button"
class="text-sm text-zinc-400"
>
<i18n-t
keypath="community.friends.viewAll"
tag="span"
scope="global"
>
<template #arrow>
<span aria-hidden="true">{{ $t("chars.arrow") }}</span>
</template>
</i18n-t>
</NuxtLink>
</div>
</div>
</div>
<div class="flex flex-col gap-y-3 max-h-[400px] overflow-y-scroll px-2">
<!-- Incoming friend requests -- the "invites" half of the ask, surfaced
right at the entry point instead of only on the full page. -->
<div
v-if="props.friends && props.friends.incomingRequests.length > 0"
class="flex flex-col gap-y-2"
>
<h4 class="text-xs font-semibold uppercase tracking-wide text-zinc-500">
{{ $t("community.friends.incoming") }}
</h4>
<div
v-for="req in props.friends.incomingRequests"
:key="req.id"
class="flex items-center justify-between gap-x-2"
>
<div class="flex items-center gap-x-2 min-w-0">
<UserAvatar
:avatar-url="req.user.avatarUrl"
:name="req.user.displayName"
:size-px="28"
/>
<span class="text-zinc-100 text-sm truncate">{{
req.user.displayName
}}</span>
</div>
<div class="flex gap-x-1.5 shrink-0">
<button
type="button"
class="rounded-md bg-emerald-600 px-2 py-1 text-xs font-semibold text-white hover:bg-emerald-500 duration-200"
@click="accept(req.id)"
>
{{ $t("community.friends.accept") }}
</button>
<button
type="button"
class="rounded-md bg-zinc-700 px-2 py-1 text-xs font-semibold text-zinc-200 hover:bg-zinc-600 duration-200"
@click="decline(req.id)"
>
{{ $t("community.friends.decline") }}
</button>
</div>
</div>
<div class="h-0.5 rounded-full w-full bg-zinc-800 mt-1" />
</div>
<!-- Friends list, with a direct entry into chat with each one. -->
<div
v-for="friend in props.friends?.friends ?? []"
:key="friend.id"
class="flex items-center justify-between gap-x-2"
>
<NuxtLink
:to="`/community/profile/${friend.username}`"
class="flex items-center gap-x-2 min-w-0"
>
<UserAvatar
:avatar-url="friend.avatarUrl"
:name="friend.displayName"
:size-px="28"
/>
<span class="text-zinc-100 text-sm truncate">{{
friend.displayName
}}</span>
</NuxtLink>
<NuxtLink
:to="`/community/chat?dm=${friend.id}`"
class="text-xs text-blue-400 hover:text-blue-300 shrink-0"
>
{{ $t("community.friends.message") }}
</NuxtLink>
</div>
</div>
<div
v-if="
props.friends &&
props.friends.friends.length === 0 &&
props.friends.incomingRequests.length === 0
"
class="text-sm text-zinc-400 p-3 text-center w-full"
>
{{ $t("community.friends.noFriends") }}
</div>
<div
v-else-if="!props.friends"
class="text-sm text-zinc-400 p-3 text-center w-full"
>
{{ $t("common.srLoading") }}
</div>
</PanelWidget>
</template>
<script setup lang="ts">
import type { FriendsListResult } from "~/composables/community-friends";
const props = defineProps<{
friends: FriendsListResult | null;
}>();
async function accept(requestId: string) {
await $dropFetch(`/api/v1/community/friends/requests/${requestId}/accept`, {
method: "POST",
});
await refreshCommunityFriends();
}
async function decline(requestId: string) {
await $dropFetch(`/api/v1/community/friends/requests/${requestId}/decline`, {
method: "POST",
});
await refreshCommunityFriends();
}
</script>