userlist, announcement and other misc stuff

Signed-off-by: MiniDigger <admin@minidigger.me>
This commit is contained in:
MiniDigger 2021-03-13 17:40:57 +01:00
parent 7e5e2b36af
commit 0e26eed9b5
19 changed files with 124 additions and 45 deletions

View File

@ -1,4 +1,5 @@
@import '~vuetify/src/styles/styles.sass';
@import 'utils';
.text-transform-unset {
text-transform: unset;

View File

@ -7,3 +7,8 @@
@mixin basic-border() {
border: 1px solid $lighter;
}
.flex-right {
margin-left: auto;
order: 2;
}

View File

@ -117,7 +117,6 @@ export default class MarkdownEditor extends Vue {
<style lang="scss" scoped>
.page-btn {
// TODO dynamic based on which buttons are shown
left: 0;
&.edit-btn,

View File

@ -1,12 +1,44 @@
<template>
<!-- todo memberlist -->
<div>Memberlist</div>
<v-card>
<v-card-title>
{{ $t('project.members') }}
<v-btn v-if="canEdit" icon class="flex-right" :to="manageUrl">
<v-icon> mdi-pencil </v-icon>
</v-btn>
</v-card-title>
<v-card-text>
<v-list v-if="members">
<v-list-item v-for="member in members" :key="member.user.name">
<UserAvatar :username="member.user.name" clazz="user-avatar-xs"></UserAvatar>
<NuxtLink :to="'/' + member.user.name">{{ member.user.name }}</NuxtLink>
<span class="flex-right">{{ member.role.role.title }}</span>
</v-list-item>
</v-list>
<div v-else class="text-center py-4">
<v-progress-circular indeterminate />
</div>
</v-card-text>
</v-card>
</template>
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator';
@Component({})
export default class MemberList extends Vue {}
import { Prop } from 'vue-property-decorator';
import { ProjectMember } from 'hangar-internal';
import UserAvatar from '~/components/UserAvatar.vue';
@Component({
components: { UserAvatar },
})
export default class MemberList extends Vue {
@Prop()
members!: ProjectMember[];
@Prop()
canEdit!: Boolean;
@Prop()
manageUrl!: String;
}
</script>
<style lang="scss" scoped></style>

View File

@ -29,10 +29,6 @@ export default class UserAvatar extends Vue {
@Prop()
clazz!: PropType<'user-avatar-md' | 'user-avatar-sm' | 'user-avatar-xs'>;
// attribute map // TODO implement
@Prop()
attr!: Object;
@Prop()
href!: String;

View File

@ -1,10 +1,9 @@
<template>
<v-list>
<v-list-item v-for="user in users" :key="user.id">
<UserAvatar :username="user.name" :avatar-url="$util.avatarUrl(user.name)" clazz="user-avatar-xs" />
{{ user.name }}
ROLE HERE
<!-- todo role -->
<v-list-item v-for="member in users" :key="member.user.name">
<UserAvatar :username="member.user.name" clazz="user-avatar-xs"></UserAvatar>
<NuxtLink :to="'/' + member.user.name">{{ member.user.name }}</NuxtLink>
<span class="flex-right">{{ member.role.role.title }}</span>
</v-list-item>
<v-divider />
<v-list-item>
@ -17,15 +16,26 @@
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator';
import { User } from 'hangar-api';
import { ProjectMember } from 'hangar-internal';
import UserAvatar from '~/components/UserAvatar.vue';
import { RoleCategory } from '~/types/enums';
// TODO v-model for users
@Component({
components: { UserAvatar },
})
export default class UserSelectionForm extends Vue {
users: Array<User> = [this.$util.dummyUser()];
users: Array<ProjectMember> = [
{
user: this.$util.dummyUser(),
role: {
accepted: true,
id: -1,
createdAt: '',
role: { title: 'Owner', value: 'owner', roleId: -1, category: RoleCategory.PROJECT, permission: '', color: { hex: '' } },
},
},
];
}
</script>

View File

@ -3,8 +3,7 @@
<v-container>
<v-row align="center" justify="center">
<v-col cols="12" class="d-flex justify-center">
By using this site you're accepting our <a href="#"> Terms of Service</a
><!-- todo tos link -->
By using this site you're accepting our <a href="https://papermc.io/community-guidelines"> Terms of Service</a>
</v-col>
</v-row>

View File

@ -215,7 +215,6 @@ export default class Header extends Vue {
<style lang="scss">
.v-badge--bordered.header-badge .v-badge__badge::after {
// TODO variable for header background
border-color: #272727 !important;
}
</style>

View File

@ -3,7 +3,9 @@
<Header />
<v-main>
<v-container>
<Announcement v-for="(announcement, idx) in announcements" :key="idx" :announcement="announcement" />
<template v-if="announcements">
<Announcement v-for="(announcement, idx) in announcements" :key="idx" :announcement="announcement" />
</template>
<DonationResult />
<nuxt />
@ -33,13 +35,10 @@ import DonationResult from '~/components/donation/DonationResult.vue';
})
export default class DefaultLayout extends Vue {
title = 'Hangar';
// TODO fetch from server
announcements = [
{
text:
'This is a staging server for testing purposes. Data could be deleted at any time. email confirmations are disabled. If you wanna help test, sneak into #hangar-dev',
color: 'red lighten-1',
},
];
announcements: Announcement[] = [];
async fetch() {
this.announcements = await this.$api.requestInternal<Announcement[]>('data/announcements', false).catch<any>(this.$util.handlePageRequestError);
}
}
</script>

View File

@ -68,6 +68,7 @@ const msgs: LocaleMessageObject = {
noStargazers: 'There are no stargazers on this project yet :/',
watchers: 'Watchers',
noWatchers: 'There are no watchers on this project yet :/',
members: 'Members',
category: {
info: 'Category: {0}',
admin_tools: 'Admin Tools',

View File

@ -93,14 +93,12 @@
<v-col cols="12">
<ProjectPageList :project="project" />
</v-col>
<!-- todo member list -->
<v-col cols="12">
<MemberList />
<!-- <MemberList-->
<!-- :filtered-members-prop="filteredMembers"-->
<!-- :can-manage-members="canManageMembers"-->
<!-- :settings-call="ROUTES.parse('PROJECTS_SHOW_SETTINGS', project.ownerName, project.slug)"-->
<!-- ></MemberList>-->
<MemberList
:members="project.members"
:can-edit="$perms.canManageSubjectMembers"
:manage-url="`/${project.namespace.owner}/${project.namespace.slug}/manage`"
/>
</v-col>
</v-row>
</v-col>
@ -109,8 +107,9 @@
<script lang="ts">
import { Component } from 'nuxt-property-decorator';
import { ProjectPage } from 'hangar-internal';
import { HangarProject, ProjectPage } from 'hangar-internal';
import { Context } from '@nuxt/types';
import { Prop } from 'vue-property-decorator';
import MarkdownEditor from '~/components/MarkdownEditor.vue';
import Tag from '~/components/Tag.vue';
import DonationModal from '~/components/donation/DonationModal.vue';
@ -124,6 +123,9 @@ import ProjectPageList from '~/components/projects/ProjectPageList.vue';
components: { ProjectPageList, NewPageModal, Markdown, MemberList, DonationModal, MarkdownEditor, Tag },
})
export default class DocsPage extends DocPageMixin {
@Prop()
project!: HangarProject;
async asyncData({ $api, params, $util }: Context) {
const page = await $api.requestInternal<ProjectPage>(`pages/page/${params.author}/${params.slug}`, false).catch<any>($util.handlePageRequestError);
return { page };

View File

@ -191,10 +191,13 @@
</v-card>
</v-col>
<v-col cols="12" md="4">
<!-- todo memberlist (like index page -->
<v-card>
<v-card-title>Members</v-card-title>
<v-card-text>...</v-card-text>
<v-card-title>
{{ $t('project.members') }}
</v-card-title>
<v-card-text>
<UserSelectionForm :users="project.members" />
</v-card-text>
</v-card>
</v-col>
</v-row>
@ -202,11 +205,16 @@
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator';
import { HangarProject } from 'hangar-internal';
import { Prop } from 'vue-property-decorator';
import { ProjectPermission } from '~/utils/perms';
import { NamedPermission, ProjectCategory } from '~/types/enums';
import { RootState } from '~/store';
@Component
import MemberList from '~/components/MemberList.vue';
import UserSelectionForm from '~/components/UserSelectionForm.vue';
@Component({
components: { UserSelectionForm, MemberList },
})
@ProjectPermission(NamedPermission.EDIT_SUBJECT_SETTINGS)
export default class ProjectManagePage extends Vue {
apiKey = '';
@ -228,6 +236,9 @@ export default class ProjectManagePage extends Vue {
category: ProjectCategory.UNDEFINED,
};
@Prop()
project!: HangarProject;
get categoryIcon() {
return (this.$store.state as RootState).projectCategories.get(this.form.category)?.icon;
}

View File

@ -1,6 +1,6 @@
declare module 'hangar-internal' {
import { Table, FlagReason } from 'hangar-internal';
import { Project, User } from 'hangar-api';
import { Project, Role, User } from 'hangar-api';
interface ProjectOwner {
id: number;
@ -25,9 +25,19 @@ declare module 'hangar-internal' {
children: HangarProjectPage[];
}
interface ProjectRole extends Table {
accepted: true;
role: Role;
}
interface ProjectMember {
user: User;
role: ProjectRole;
}
interface HangarProject extends Project, Table {
owner: ProjectOwner;
members: object[];
members: ProjectMember[];
lastVisibilityChangeComment: string;
lastVisibilityChangeUserName: string;
info: HangarProjectInfo;

View File

@ -1,7 +1,7 @@
package io.papermc.hangar.config.hangar;
import io.papermc.hangar.HangarApplication;
import io.papermc.hangar.modelold.Announcement;
import io.papermc.hangar.model.Announcement;
import io.papermc.hangar.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;

View File

@ -12,6 +12,7 @@ import io.papermc.hangar.model.common.NamedPermission;
import io.papermc.hangar.model.common.Platform;
import io.papermc.hangar.model.common.projects.Category;
import io.papermc.hangar.model.common.projects.FlagReason;
import io.papermc.hangar.model.Announcement;
import io.papermc.hangar.security.annotations.Anyone;
import io.papermc.hangar.service.internal.projects.PlatformService;
import org.springframework.beans.factory.annotation.Autowired;
@ -24,6 +25,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
@Controller
@ -114,5 +116,10 @@ public class BackendDataController {
public ResponseEntity<HangarConfig.Sponsor> getSponsor() {
return ResponseEntity.ok(config.getSponsors().get(ThreadLocalRandom.current().nextInt(config.getSponsors().size())));
}
@GetMapping("/announcements")
public ResponseEntity<List<Announcement>> getAnnouncements() {
return ResponseEntity.ok(config.getAnnouncements());
}
}

View File

@ -1,4 +1,4 @@
package io.papermc.hangar.modelold;
package io.papermc.hangar.model;
import java.util.Objects;
import java.util.StringJoiner;

View File

@ -1,5 +1,7 @@
package io.papermc.hangar.model.common.roles;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.papermc.hangar.db.customtypes.RoleCategory;
import io.papermc.hangar.model.common.Color;
import io.papermc.hangar.model.common.Permission;
@ -8,6 +10,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.postgresql.shaded.com.ongres.scram.common.util.Preconditions;
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum OrganizationRole implements Role<OrganizationRoleTable> {
ORGANIZATION_SUPPORT("Organization_Support", 28, Permission.PostAsOrganization.add(Permission.IsOrganizationMember), "Support", Color.TRANSPARENT),

View File

@ -1,5 +1,7 @@
package io.papermc.hangar.model.common.roles;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.papermc.hangar.db.customtypes.RoleCategory;
import io.papermc.hangar.model.common.Color;
import io.papermc.hangar.model.common.Permission;
@ -8,6 +10,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.postgresql.shaded.com.ongres.scram.common.util.Preconditions;
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum ProjectRole implements Role<ProjectRoleTable> {
PROJECT_SUPPORT("Project_Support", 22, Permission.IsProjectMember, "Support", Color.TRANSPARENT),

View File

@ -1,5 +1,6 @@
package io.papermc.hangar.model.db.roles;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.papermc.hangar.model.common.roles.Role;
import io.papermc.hangar.model.db.Table;
@ -9,6 +10,7 @@ import java.time.OffsetDateTime;
public abstract class ExtendedRoleTable<R extends Role<? extends IRoleTable<R>>> extends Table implements IRoleTable<R> {
protected final long userId;
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
protected final R role;
protected boolean accepted;