mirror of
https://github.com/HangarMC/Hangar.git
synced 2025-02-05 14:40:33 +08:00
start with user page
This commit is contained in:
parent
44a4015eed
commit
caea626d30
@ -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
|
||||
);
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -1,3 +1,4 @@
|
||||
<template>
|
||||
<h1>version</h1>
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
|
@ -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" />
|
||||
|
||||
{{ orgName }}
|
||||
|
||||
{{ orgRole.role.title }}
|
||||
|
||||
<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>
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user