Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add admin profile page #68

Merged
merged 1 commit into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion web/admin/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ async function login() {
</script>

<template>
<NuxtPage v-if="user" />
<div v-if="user" class="max-w-[800px] py-4 px-3 mx-auto">
<core-nav />
<NuxtPage />
</div>
<div v-else class="flex items-center justify-center fixed w-full h-full">
<div
class="mx-auto bg-gray-50 border border-gray-400 dark:border-gray-700 dark:bg-gray-800 py-4 px-5 rounded-lg w-[400px] max-w-[80vw]"
Expand Down
9 changes: 4 additions & 5 deletions web/admin/components/core/nav.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
<script setup>
import { logout, newAgent } from "~/lib/auth";
import { logout } from "~/lib/auth";

const user = await useUser();
const agent = newAgent();
const profile = await agent.getProfile({ actor: user.value.did });
const profile = await useProfile();
</script>

<template>
Expand All @@ -21,9 +19,10 @@ const profile = await agent.getProfile({ actor: user.value.did });
</nuxt-link>
<h1 class="text-xl font-bold">Admin</h1>
<div class="ml-auto flex items-center gap-2">
<shared-search />
<img
class="rounded-full"
:src="profile.data.avatar"
:src="profile.avatar"
height="32"
width="32"
alt=""
Expand Down
15 changes: 15 additions & 0 deletions web/admin/components/icon/check.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<svg
viewBox="0 0 24 24"
width="14"
height="14"
stroke="currentColor"
stroke-width="2"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
class="css-i6dzq1"
>
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
</template>
16 changes: 16 additions & 0 deletions web/admin/components/icon/cross.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<svg
viewBox="0 0 24 24"
width="14"
height="14"
stroke="currentColor"
stroke-width="2"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
class="css-i6dzq1"
>
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</template>
17 changes: 17 additions & 0 deletions web/admin/components/icon/search.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-search"
>
<circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
</template>
8 changes: 4 additions & 4 deletions web/admin/components/shared/avatar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ defineProps<{ url?: string }>();
v-if="url"
class="rounded-full"
:src="url"
height="64"
width="64"
height="72"
width="72"
alt=""
/>
<svg
v-else
width="64"
height="64"
width="72"
height="72"
viewBox="0 0 24 24"
fill="none"
stroke="none"
Expand Down
36 changes: 36 additions & 0 deletions web/admin/components/shared/search.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<script setup>
import { newAgent } from "~/lib/auth";

const term = ref();

const search = async () => {
const agent = newAgent();
const { data, success } = await agent
.getProfile({ actor: term.value })
.catch(() => ({ success: false }));
if (!success) {
alert("Could not find user. Please check handle or did, and try again.");
return;
}
useRouter().push(`/users/${data.did}`);
term.value = "";
};
</script>

<template>
<div class="flex">
<input
v-model="term"
class="py-1 px-2 rounded-l-lg border border-gray-300 text-black"
type="text"
placeholder="User handle or did"
@keydown="$event.key === 'Enter' ? search() : null"
/>
<button
class="text-white bg-blue-400 dark:bg-blue-500 rounded-r-lg px-1"
@click="search"
>
<icon-search />
</button>
</div>
</template>
42 changes: 37 additions & 5 deletions web/admin/components/user-card.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
<script lang="ts" setup>
import { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs";
import { newAgent } from "~/lib/auth";
import { ActorStatus } from "../../proto/bff/v1/moderation_service_pb";

const props = defineProps<{ did: string; loading: boolean; pending: number }>();
const props = defineProps<{
did: string;
loading: boolean;
pending: number;
variant: "queue" | "profile";
}>();
defineEmits(["accept", "reject"]);

const api = await useAPI();
const isArtist = ref(false);
const status = ref<ActorStatus | undefined>();
const agent = newAgent();
const data = ref<ProfileViewDetailed>();
const loadProfile = async () => {
const result = await agent.getProfile({
actor: props.did,
});
data.value = result.data;
const { actor } = await api
.getActor({ did: result.data.did })
.catch(() => ({ actor: undefined }));
isArtist.value = Boolean(actor?.isArtist);
status.value = actor?.status;
};

watch(
Expand All @@ -24,7 +38,7 @@ await loadProfile();

<template>
<shared-card v-if="data" :class="{ loading }">
<div class="flex items-center gap-3 pt-2 mb-5">
<div v-if="variant === 'queue'" class="flex items-center gap-3 pt-2 mb-5">
<button
class="px-3 py-2 bg-blue-400 dark:bg-blue-600 rounded-lg hover:bg-blue-500 dark:hover:bg-blue-700 disabled:bg-blue-300 disabled:dark:bg-blue-500 disabled:cursor-not-allowed"
:disabled="loading"
Expand All @@ -47,7 +61,7 @@ await loadProfile();
>
</div>

<div class="flex gap-3 items-center">
<div class="flex gap-3 items-center mb-5">
<shared-avatar :url="data.avatar" />
<div class="flex flex-col">
<div class="text-lg">{{ data.displayName || data.handle }}</div>
Expand All @@ -73,9 +87,25 @@ await loadProfile();
<span class="text-gray-600 dark:text-gray-400">posts</span>
</span>
</div>
<div v-if="variant === 'profile'" class="meta">
<span class="meta-item inline-flex items-center">
<icon-check
v-if="status === ActorStatus.APPROVED"
class="text-green-500"
/>
<icon-cross v-else class="text-red-500" />
<span class="text-gray-600 dark:text-gray-400">In list</span>
</span>
<span class="meta-item inline-flex items-center">
<icon-check v-if="isArtist" class="text-green-500" />
<icon-cross v-else class="text-red-500" />
<span class="text-gray-600 dark:text-gray-400">Artist</span>
</span>
</div>
</div>
</div>
<div class="mt-5">

<div>
<shared-bsky-description
v-if="data.description"
:description="data.description"
Expand All @@ -89,7 +119,9 @@ await loadProfile();

<style scoped>
.meta .meta-item:not(:last-child)::after {
content: " · ";
content: "·";
@apply px-1;
@apply inline-block;
text-decoration: none;
}

Expand Down
2 changes: 1 addition & 1 deletion web/admin/composables/useAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ModerationService } from "../../proto/bff/v1/moderation_service_connect
export default async function () {
const user = await useUser();
const transport = createConnectTransport({
baseUrl: "https://feed.furryli.st",
baseUrl: "http://localhost:1337",

fetch(input, data = {}) {
(data.headers as Headers).set(
Expand Down
16 changes: 16 additions & 0 deletions web/admin/composables/useProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs";
import { newAgent } from "~/lib/auth";

export default async function () {
const user = await useUser();
const profile = useState<ProfileViewDetailed>("profile");

const agent = newAgent();

if (user.value && !profile.value) {
const { data } = await agent.getProfile({ actor: user.value.did });
profile.value = data;
}

return profile;
}
22 changes: 10 additions & 12 deletions web/admin/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,14 @@ await nextActor();
</script>

<template>
<div class="max-w-[800px] py-4 px-3 mx-auto">
<core-nav />
<user-card
v-if="actor"
:did="actor.did"
:loading="loading"
:pending="pending"
@accept="accept"
@reject="reject"
/>
<shared-card v-else> No user is in the queue. </shared-card>
</div>
<user-card
v-if="actor"
:did="actor.did"
:loading="loading"
:pending="pending"
variant="queue"
@accept="accept"
@reject="reject"
/>
<shared-card v-else> No user is in the queue. </shared-card>
</template>
12 changes: 12 additions & 0 deletions web/admin/pages/users/[did].vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script setup>
import { newAgent } from "~/lib/auth";

const agent = newAgent();
const { data } = await agent.getProfile({ actor: useRoute().params.did });
</script>

<template>
<div>
<user-card class="mb-5" :did="data.did" variant="profile" />
</div>
</template>