mirror of
https://github.com/HangarMC/Hangar.git
synced 2024-12-27 07:03:26 +08:00
c420e5dd33
Signed-off-by: MiniDigger <admin@minidigger.me> #382
216 lines
8.5 KiB
Vue
216 lines
8.5 KiB
Vue
<template>
|
|
<v-row>
|
|
<v-col cols="12" md="7">
|
|
<h1>{{ $t('notifications.title') }}</h1>
|
|
<v-select v-model="filters.notification" dense filled :items="filter">
|
|
<template #append-outer>
|
|
<v-btn
|
|
v-if="filteredNotifications.length && filters.notification === 'unread'"
|
|
color="info"
|
|
text
|
|
class="input-append-btn"
|
|
@click.stop="markAllAsRead"
|
|
>
|
|
{{ $t('notifications.readAll') }}
|
|
</v-btn>
|
|
</template>
|
|
</v-select>
|
|
<v-list v-if="filteredNotifications.length">
|
|
<v-list-item v-for="notification in filteredNotifications" :key="notification.id" class="notification">
|
|
<v-list-item-title :class="`n-${notification.type}`">
|
|
{{ $t(notification.message[0], notification.message.slice(1)) }}
|
|
</v-list-item-title>
|
|
<v-list-item-action v-if="!notification.read">
|
|
<v-btn icon class="success" @click.stop="markNotificationRead(notification)">
|
|
<v-icon>mdi-check</v-icon>
|
|
</v-btn>
|
|
</v-list-item-action>
|
|
</v-list-item>
|
|
</v-list>
|
|
<div v-else class="red--text text--lighten-2">
|
|
{{ $t(`notifications.empty.${filters.notification}`) }}
|
|
</div>
|
|
</v-col>
|
|
<v-divider vertical class="mt-2" />
|
|
<v-col v-if="!$fetchState.pending" cols="12" md="5">
|
|
<h1>{{ $t('notifications.invites') }}</h1>
|
|
<v-select v-model="filters.invite" dense filled :items="inviteFilter"></v-select>
|
|
<v-list v-if="filteredInvites.length">
|
|
<v-list-item v-for="(invite, index) in filteredInvites" :key="index">
|
|
<v-list-item-title>
|
|
{{ $t(!invite.accepted ? 'notifications.invited' : 'notifications.inviteAccepted', [invite.type]) }}:
|
|
<NuxtLink :to="invite.url" exact>{{ invite.name }}</NuxtLink>
|
|
</v-list-item-title>
|
|
<template v-if="!invite.accepted">
|
|
<v-list-item-action>
|
|
<v-tooltip bottom>
|
|
<template #activator="{ on }">
|
|
<v-btn icon class="success" v-on="on" @click="updateInvite(invite, 'accept')">
|
|
<v-icon>mdi-check</v-icon>
|
|
</v-btn>
|
|
</template>
|
|
<span>{{ $t('notifications.invite.btns.accept') }}</span>
|
|
</v-tooltip>
|
|
</v-list-item-action>
|
|
<v-list-item-action>
|
|
<v-tooltip bottom>
|
|
<template #activator="{ on }">
|
|
<v-btn icon color="error" v-on="on" @click="updateInvite(invite, 'decline')">
|
|
<v-icon>mdi-close</v-icon>
|
|
</v-btn>
|
|
</template>
|
|
<span>{{ $t('notifications.invite.btns.decline') }}</span>
|
|
</v-tooltip>
|
|
</v-list-item-action>
|
|
</template>
|
|
<v-list-item-action v-else>
|
|
<v-tooltip bottom>
|
|
<template #activator="{ on }">
|
|
<v-btn icon class="warning" v-on="on" @click="updateInvite(invite, 'unaccept')">
|
|
<v-icon>mdi-undo</v-icon>
|
|
</v-btn>
|
|
</template>
|
|
<span>{{ $t('notifications.invite.btns.unaccept') }}</span>
|
|
</v-tooltip>
|
|
</v-list-item-action>
|
|
</v-list-item>
|
|
</v-list>
|
|
<div v-else class="red--text text--lighten-2">
|
|
{{ $t('notifications.empty.invites') }}
|
|
</div>
|
|
</v-col>
|
|
</v-row>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { Component } from 'nuxt-property-decorator';
|
|
import { HangarNotification, Invite, Invites } from 'hangar-internal';
|
|
import { LoggedIn } from '~/utils/perms';
|
|
import { HangarComponent } from '~/components/mixins';
|
|
|
|
@Component({})
|
|
@LoggedIn
|
|
export default class NotificationsPage extends HangarComponent {
|
|
notifications: HangarNotification[] = [];
|
|
invites = {} as Invites;
|
|
filters = {
|
|
notification: 'unread' as 'unread' | 'read' | 'all',
|
|
invite: 'all' as 'organizations' | 'projects' | 'all',
|
|
};
|
|
|
|
head() {
|
|
return this.$seo.head('Notifications', null, this.$route, null);
|
|
}
|
|
|
|
get filteredNotifications(): HangarNotification[] {
|
|
switch (this.filters.notification) {
|
|
case 'unread':
|
|
return this.notifications.filter((n) => !n.read);
|
|
case 'read':
|
|
return this.notifications.filter((n) => n.read);
|
|
case 'all':
|
|
return this.notifications;
|
|
}
|
|
}
|
|
|
|
get filteredInvites(): Invite[] {
|
|
switch (this.filters.invite) {
|
|
case 'projects':
|
|
return this.invites.project;
|
|
case 'organizations':
|
|
return this.invites.organization;
|
|
case 'all':
|
|
return [...this.invites.project, ...this.invites.organization];
|
|
}
|
|
}
|
|
|
|
get filter() {
|
|
return [
|
|
{ text: this.$t('notifications.unread'), value: 'unread' },
|
|
{ text: this.$t('notifications.read'), value: 'read' },
|
|
{ text: this.$t('notifications.all'), value: 'all' },
|
|
];
|
|
}
|
|
|
|
get inviteFilter() {
|
|
return [
|
|
{ text: this.$t('notifications.invite.organizations'), value: 'organizations' },
|
|
{ text: this.$t('notifications.invite.projects'), value: 'projects' },
|
|
{ text: this.$t('notifications.invite.all'), value: 'all' },
|
|
];
|
|
}
|
|
|
|
markAllAsRead() {
|
|
this.notifications
|
|
.filter((n) => !n.read)
|
|
.forEach((n) => {
|
|
this.markNotificationRead(n, false);
|
|
});
|
|
}
|
|
|
|
markNotificationRead(notification: HangarNotification, router: boolean = true) {
|
|
this.$api
|
|
.requestInternal(`notifications/${notification.id}`, true, 'post')
|
|
.then(() => {
|
|
this.$delete(
|
|
this.notifications,
|
|
this.notifications.findIndex((n) => n.id === notification.id)
|
|
);
|
|
if (notification.action && router) {
|
|
this.$router.push(notification.action);
|
|
}
|
|
this.$auth.refreshUser();
|
|
})
|
|
.catch(this.$util.handleRequestError);
|
|
}
|
|
|
|
updateInvite(invite: Invite, status: 'accept' | 'decline' | 'unaccept') {
|
|
this.$api
|
|
.requestInternal(`invites/${invite.type}/${invite.roleTableId}/${status}`, true, 'post')
|
|
.then(() => {
|
|
if (status === 'accept') {
|
|
this.$set(invite, 'accepted', true);
|
|
} else if (status === 'unaccept') {
|
|
this.$set(invite, 'accepted', false);
|
|
} else {
|
|
this.$delete(this.invites[invite.type], this.invites[invite.type].indexOf(invite));
|
|
}
|
|
this.$auth.refreshUser();
|
|
this.$util.success(this.$t(`notifications.invite.msgs.${status}`, [invite.name]));
|
|
})
|
|
.catch(this.$util.handleRequestError);
|
|
}
|
|
|
|
async fetch() {
|
|
this.notifications = (await this.$api.requestInternal<HangarNotification[]>('notifications').catch(this.$util.handleRequestError)) || [];
|
|
this.invites = (await this.$api.requestInternal<Invites>('invites').catch(this.$util.handleRequestError)) || { project: [], organization: [] };
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import '~vuetify/src/styles/styles';
|
|
|
|
.notification {
|
|
.n-neutral {
|
|
color: map-deep-get($material-dark, 'text', 'secondary');
|
|
}
|
|
|
|
.n-success {
|
|
color: map-get($green, 'accent-3');
|
|
}
|
|
|
|
.n-info {
|
|
color: var(--v-info-lighten1);
|
|
}
|
|
|
|
.n-warning {
|
|
color: map-get($deep-orange, 'accent-3');
|
|
}
|
|
|
|
.n-error {
|
|
color: map-get($red, 'accent-4');
|
|
}
|
|
}
|
|
</style>
|