start with user page

This commit is contained in:
MiniDigger 2022-03-25 22:13:39 +01:00
parent 44a4015eed
commit caea626d30
5 changed files with 146 additions and 14 deletions

View File

@ -1,7 +1,18 @@
import { useApi, useInternalApi } from "~/composables/useApi";
import { PaginatedResult, Project, User } from "hangar-api";
import { PaginatedResult, Project, ProjectCompact, Role, User } from "hangar-api";
import { useInitialState } from "~/composables/useInitialState";
import { Flag, HangarNotification, HangarProject, HealthReport, Invites, LoggedAction, Note, ProjectChannel, ReviewQueueEntry } from "hangar-internal";
import {
Flag,
HangarNotification,
HangarProject,
HealthReport,
Invites,
LoggedAction,
Note,
ProjectChannel,
ReviewQueueEntry,
RoleTable,
} from "hangar-internal";
export async function useProjects(pagination = { limit: 25, offset: 0 }, blocking = true) {
return useInitialState("useProjects", () => useApi<PaginatedResult<Project>>("projects", false, "get", pagination), blocking);
@ -70,3 +81,35 @@ export async function useVersionApprovals(blocking = true) {
blocking
);
}
export async function useOrgVisibility(user: string, blocking = true) {
return useInitialState(
"useOrgVisibility",
() => useInternalApi<{ [key: string]: boolean }>(`organizations/${user}/userOrganizationsVisibility`, true),
blocking
);
}
export async function useUserData(user: string, blocking = true) {
return useInitialState(
"useUserData",
async () => {
// noinspection ES6MissingAwait
const data = await Promise.all([
useApi<PaginatedResult<ProjectCompact>>(`users/${user}/starred`, false),
useApi<PaginatedResult<ProjectCompact>>(`users/${user}/watching`, false),
useApi<PaginatedResult<Project>>(`projects`, false, "get", {
owner: user,
}),
useInternalApi<{ [key: string]: RoleTable }>(`organizations/${user}/userOrganizations`, false),
] as Promise<any>[]);
return {
starred: data[0] as PaginatedResult<ProjectCompact>,
watching: data[1] as PaginatedResult<ProjectCompact>,
projects: data[2] as PaginatedResult<Project>,
organizations: data[3] as { [key: string]: RoleTable },
};
},
blocking
);
}

View File

@ -10,10 +10,13 @@ const ctx = useContext();
const i18n = useI18n();
const user = await useUser(useRoute().params.user as string).catch((e) => handleRequestError(e, ctx, i18n));
if (!user) {
// TODO check if user is an org here
await useRouter().push(useErrorRedirect(useRoute(), 404, "Not found"));
}
</script>
<template>
<router-view v-if="user" :user="user"></router-view>
<router-view v-if="user" v-slot="{ Component }">
<component :is="Component" :user="user" :dum="user" />
</router-view>
</template>

View File

@ -1,3 +1,4 @@
<template>
<h1>version</h1>
<router-view></router-view>
</template>

View File

@ -1,18 +1,99 @@
<script setup lang="ts">
import { PropType } from "vue";
import { User } from "hangar-api";
import ProjectList from "~/components/ProjectList.vue";
import Card from "~/components/design/Card.vue";
import { useI18n } from "vue-i18n";
import { avatarUrl } from "~/composables/useUrlHelper";
import UserAvatar from "~/components/UserAvatar.vue";
import Link from "~/components/design/Link.vue";
import MemberList from "~/components/projects/MemberList.vue";
import { useContext } from "vite-ssr/vue";
import { useOrgVisibility, useUserData } from "~/composables/useApiHelper";
import { useBackendDataStore } from "~/store/backendData";
import { useAuthStore } from "~/store/auth";
import { useRoute } from "vue-router";
defineProps({
user: {
type: Object as PropType<User>,
required: true,
},
});
const props = defineProps<{
user: User;
}>();
const i18n = useI18n();
const ctx = useContext();
const route = useRoute();
const { starred, watching, projects, organizations } = (await useUserData(props.user.name)).value || {};
let organizationVisibility = null;
if (props.user.name === useAuthStore().user?.name) {
organizationVisibility = await useOrgVisibility(props.user.name);
}
const orgRoles = useBackendDataStore().orgRoles;
</script>
<template>
<div>
user child
{{ user }}
<!-- todo user header -->
<div class="flex gap-4">
<div class="flex-basis-full md:flex-basis-8/12 flex-grow">
<ProjectList :projects="projects"></ProjectList>
</div>
<div class="flex-basis-full md:flex-basis-4/12 flex-grow">
<template v-if="!user.isOrganization">
<Card class="mb-4">
<template #header>
{{ i18n.t("author.orgs") }}
<!-- todo org visibility modal -->
</template>
<ul>
<li v-for="(orgRole, orgName) in organizations" :key="orgName">
<router-link :to="orgName" class="flex">
<UserAvatar :username="orgName" :avatar-url="avatarUrl(orgName)" size="xs" />
&nbsp;
{{ orgName }}
&nbsp;
{{ orgRole.role.title }}
&nbsp;
<span v-if="organizationVisibility && organizationVisibility[orgName]"> Hidden </span>
</router-link>
</li>
</ul>
<span v-if="!organizations || Object.keys(organizations).length === 0">
{{ i18n.t("author.noOrgs", [props.user.name]) }}
</span>
</Card>
<Card class="mb-4">
<template #header>{{ i18n.t("author.stars") }}</template>
<ul>
<li v-for="star in starred.result" :key="star.name">
<Link :to="'/' + star.namespace.owner + '/' + star.namespace.slug">
{{ star.namespace.owner }}/<strong>{{ star.name }}</strong>
</Link>
</li>
<span v-if="!starred || starred.result.length === 0">
{{ i18n.t("author.noStarred", [props.user.name]) }}
</span>
</ul>
</Card>
<Card>
<template #header>{{ i18n.t("author.watching") }}</template>
<ul>
<li v-for="watch in watching.result" :key="watch.name">
<Link :to="'/' + watch.namespace.owner + '/' + watch.namespace.slug"
>{{ watch.namespace.owner }}/<strong>{{ watch.name }}</strong></Link
>
</li>
<span v-if="!watching || watching.result.length === 0">
{{ i18n.t("author.noStarred", [props.user.name]) }}
</span>
</ul>
</Card>
</template>
<MemberList v-else :members="organization.members" :roles="orgRoles" :org="true" />
</div>
</div>
</template>

View File

@ -4,7 +4,7 @@ import { computed, ref } from "vue";
import { IPlatform, IProjectCategory, IPrompt, IVisibility } from "hangar-internal";
import { NamedPermission, Platform, ProjectCategory, Prompt } from "~/types/enums";
import { Announcement as AnnouncementObject, Announcement, IPermission } from "hangar-api";
import { Announcement as AnnouncementObject, Announcement, IPermission, Role } from "hangar-api";
import { fetchIfNeeded, useInternalApi } from "~/composables/useApi";
interface Validation {
@ -40,6 +40,7 @@ export const useBackendDataStore = defineStore("backendData", () => {
const announcements = ref<Announcement[]>([]);
const visibilities = ref<IVisibility[]>([]);
const licenses = ref<string[]>([]);
const orgRoles = ref<Role[]>([]);
async function initBackendData() {
try {
@ -79,6 +80,8 @@ export const useBackendDataStore = defineStore("backendData", () => {
await fetchIfNeeded(async () => await useInternalApi<IVisibility[]>("data/visibilities", false), visibilities);
await fetchIfNeeded(async () => await useInternalApi("data/validations", false), validations);
await fetchIfNeeded(async () => await useInternalApi("data/orgRoles", false), validations);
} catch (e) {
console.error("ERROR FETCHING BACKEND DATA");
console.error(e);
@ -97,6 +100,7 @@ export const useBackendDataStore = defineStore("backendData", () => {
licenses,
announcements,
visibilities,
orgRoles,
initBackendData,
visibleCategories,
visiblePlatforms,