Fix settings menu

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2020-06-25 11:36:35 +02:00
parent dd806896d1
commit 6797075461
No known key found for this signature in database
GPG key ID: A061B9DDE0CA0773
23 changed files with 1363 additions and 1291 deletions

View file

@ -17,6 +17,10 @@ a {
} }
} }
nav.breadcrumb ul li a {
text-decoration: none;
}
input.input { input.input {
border-color: $input-border-color !important; border-color: $input-border-color !important;
} }

View file

@ -1,24 +1,25 @@
<template> <template>
<li class="setting-menu-item" :class="{ active: isActive }"> <li class="setting-menu-item" :class="{ active: isActive }">
<router-link v-if="menuItem.to" :to="menuItem.to"> <router-link v-if="to" :to="to">
<span>{{ menuItem.title }}</span> <span>{{ title }}</span>
</router-link> </router-link>
<span v-else>{{ menuItem.title }}</span> <span v-else>{{ title }}</span>
</li> </li>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"; import { Component, Prop, Vue } from "vue-property-decorator";
import { ISettingMenuSection } from "@/types/setting-menu.model"; import { Route } from "vue-router";
@Component @Component
export default class SettingMenuItem extends Vue { export default class SettingMenuItem extends Vue {
@Prop({ required: true, type: Object }) menuItem!: ISettingMenuSection; @Prop({ required: false, type: String }) title!: string;
@Prop({ required: true, type: Object }) to!: Route;
get isActive() { get isActive() {
if (!this.menuItem.to) return false; if (!this.to) return false;
if (this.menuItem.to.name === this.$route.name) { if (this.to.name === this.$route.name) {
if (this.menuItem.to.params) { if (this.to.params) {
return this.menuItem.to.params.identityName === this.$route.params.identityName; return this.to.params.identityName === this.$route.params.identityName;
} }
return true; return true;
} }

View file

@ -1,28 +1,36 @@
<template> <template>
<li :class="{ active: sectionActive }"> <li :class="{ active: sectionActive }">
<router-link v-if="menuSection.to" :to="menuSection.to">{{ menuSection.title }}</router-link> <router-link v-if="to" :to="to">{{ title }}</router-link>
<b v-else>{{ menuSection.title }}</b> <b v-else>{{ title }}</b>
<ul> <ul>
<setting-menu-item :menu-item="item" v-for="item in menuSection.items" :key="item.title" /> <slot></slot>
</ul> </ul>
</li> </li>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"; import { Component, Prop, Vue } from "vue-property-decorator";
import { ISettingMenuSection } from "@/types/setting-menu.model";
import SettingMenuItem from "@/components/Settings/SettingMenuItem.vue"; import SettingMenuItem from "@/components/Settings/SettingMenuItem.vue";
import { Route } from "vue-router";
@Component({ @Component({
components: { SettingMenuItem }, components: { SettingMenuItem },
}) })
export default class SettingMenuSection extends Vue { export default class SettingMenuSection extends Vue {
@Prop({ required: true, type: Object }) menuSection!: ISettingMenuSection; @Prop({ required: false, type: String }) title!: string;
@Prop({ required: true, type: Object }) to!: Route;
get sectionActive(): boolean | undefined { get sectionActive() {
return ( if (this.$slots.default) {
this.menuSection.items && return this.$slots.default.some(
this.menuSection.items.some(({ to }) => to && to.name === this.$route.name) ({
componentOptions: {
// @ts-ignore
propsData: { to },
},
}) => to && to.name === this.$route.name
); );
} }
return false;
}
} }
</script> </script>

View file

@ -1,27 +1,88 @@
<template> <template>
<aside> <aside>
<ul> <ul>
<SettingMenuSection <SettingMenuSection :title="$t('Account')" :to="{ name: RouteName.ACCOUNT_SETTINGS }">
v-for="section in menuValue" <SettingMenuItem
:key="section.title" :title="this.$t('General')"
:menu-section="section" :to="{ name: RouteName.ACCOUNT_SETTINGS_GENERAL }"
/> />
<SettingMenuItem :title="$t('Preferences')" :to="{ name: RouteName.PREFERENCES }" />
<SettingMenuItem
:title="this.$t('Email notifications')"
:to="{ name: RouteName.NOTIFICATIONS }"
/>
</SettingMenuSection>
<SettingMenuSection :title="$t('Profiles')" :to="{ name: RouteName.IDENTITIES }">
<SettingMenuItem
v-for="profile in identities"
:key="profile.preferredUsername"
:title="profile.preferredUsername"
:to="{
name: RouteName.UPDATE_IDENTITY,
params: { identityName: profile.preferredUsername },
}"
/>
<SettingMenuItem :title="$t('New profile')" :to="{ name: RouteName.CREATE_IDENTITY }" />
</SettingMenuSection>
<SettingMenuSection
v-if="
[ICurrentUserRole.MODERATOR, ICurrentUserRole.ADMINISTRATOR].includes(
this.currentUser.role
)
"
:title="$t('Moderation')"
:to="{ name: RouteName.MODERATION }"
>
<SettingMenuItem :title="$t('Reports')" :to="{ name: RouteName.REPORTS }" />
<SettingMenuItem :title="$t('Moderation log')" :to="{ name: RouteName.REPORT_LOGS }" />
<SettingMenuItem :title="$t('Users')" :to="{ name: RouteName.USERS }" />
<SettingMenuItem :title="$t('Profiles')" :to="{ name: RouteName.PROFILES }" />
</SettingMenuSection>
<SettingMenuSection
v-if="this.currentUser.role == ICurrentUserRole.ADMINISTRATOR"
:title="$t('Admin')"
:to="{ name: RouteName.ADMIN }"
>
<SettingMenuItem :title="$t('Dashboard')" :to="{ name: RouteName.ADMIN_DASHBOARD }" />
<SettingMenuItem
:title="$t('Instance settings')"
:to="{ name: RouteName.ADMIN_SETTINGS }"
/>
<SettingMenuItem :title="$t('Federation')" :to="{ name: RouteName.RELAYS }" />
</SettingMenuSection>
</ul> </ul>
</aside> </aside>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"; import { Component, Prop, Vue } from "vue-property-decorator";
import SettingMenuSection from "@/components/Settings/SettingMenuSection.vue"; import SettingMenuSection from "./SettingMenuSection.vue";
import { ISettingMenuSection } from "@/types/setting-menu.model"; import SettingMenuItem from "./SettingMenuItem.vue";
import { IDENTITIES } from "../../graphql/actor";
import { IPerson, Person } from "../../types/actor";
import { CURRENT_USER_CLIENT } from "../../graphql/user";
import { ICurrentUser, ICurrentUserRole } from "../../types/current-user.model";
import RouteName from "../../router/name";
@Component({ @Component({
components: { SettingMenuSection }, components: { SettingMenuSection, SettingMenuItem },
apollo: {
identities: {
query: IDENTITIES,
update: (data) => data.identities.map((identity: IPerson) => new Person(identity)),
},
currentUser: CURRENT_USER_CLIENT,
},
}) })
export default class SettingsMenu extends Vue { export default class SettingsMenu extends Vue {
@Prop({ required: true, type: Array }) menu!: ISettingMenuSection[]; profiles = [];
get menuValue() { currentUser!: ICurrentUser;
return this.menu;
} identities!: IPerson[];
ICurrentUserRole = ICurrentUserRole;
RouteName = RouteName;
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View file

@ -106,6 +106,7 @@ export const USER_SETTINGS_FRAGMENT = gql`
export const USER_SETTINGS = gql` export const USER_SETTINGS = gql`
query UserSetting { query UserSetting {
loggedUser { loggedUser {
id
locale locale
settings { settings {
...UserSettingFragment ...UserSettingFragment

View file

@ -695,5 +695,6 @@
"contact uninformed": "contact uninformed", "contact uninformed": "contact uninformed",
"Can be an email or a link, or just plain text.": "Can be an email or a link, or just plain text.", "Can be an email or a link, or just plain text.": "Can be an email or a link, or just plain text.",
"No profiles found": "No profiles found", "No profiles found": "No profiles found",
"URL copied to clipboard": "URL copied to clipboard" "URL copied to clipboard": "URL copied to clipboard",
"Report #{reportNumber}": "Report #{reportNumber}"
} }

View file

@ -695,5 +695,6 @@
"A place for your code of conduct, rules or guidelines. You can use HTML tags.": "Une section appropriée pour votre code de conduite, règles ou lignes directrices. Vous pouvez utiliser des balises HTML.", "A place for your code of conduct, rules or guidelines. You can use HTML tags.": "Une section appropriée pour votre code de conduite, règles ou lignes directrices. Vous pouvez utiliser des balises HTML.",
"contact uninformed": "contact non renseigné", "contact uninformed": "contact non renseigné",
"Can be an email or a link, or just plain text.": "Peut être une adresse email ou bien un lien, ou alors du simple texte brut.", "Can be an email or a link, or just plain text.": "Peut être une adresse email ou bien un lien, ou alors du simple texte brut.",
"URL copied to clipboard": "URL copiée dans le presse-papiers" "URL copied to clipboard": "URL copiée dans le presse-papiers",
"Report #{reportNumber}": "Signalement #{reportNumber}"
} }

View file

@ -1,8 +0,0 @@
import { Route } from "vue-router";
export interface ISettingMenuSection {
title: string;
to: Route;
items?: ISettingMenuSection[];
parents?: ISettingMenuSection[];
}

View file

@ -1,4 +1,26 @@
<template> <template>
<div>
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li>
<router-link :to="{ name: RouteName.IDENTITIES }">{{ $t("Profiles") }}</router-link>
</li>
<li class="is-active" v-if="isUpdate && identity">
<router-link
:to="{
name: RouteName.UPDATE_IDENTITY,
params: { identityName: identity.preferredUsername },
}"
>{{ identity.name }}</router-link
>
</li>
<li class="is-active" v-else>
<router-link :to="{ name: RouteName.CREATE_IDENTITY }">{{
$t("New profile")
}}</router-link>
</li>
</ul>
</nav>
<div class="root" v-if="identity"> <div class="root" v-if="identity">
<h1 class="title"> <h1 class="title">
<span v-if="isUpdate">{{ identity.displayName() }}</span> <span v-if="isUpdate">{{ identity.displayName() }}</span>
@ -55,7 +77,9 @@
<b-field class="submit"> <b-field class="submit">
<div class="control"> <div class="control">
<button type="button" class="button is-primary" @click="submit()">{{ $t("Save") }}</button> <button type="button" class="button is-primary" @click="submit()">
{{ $t("Save") }}
</button>
</div> </div>
</b-field> </b-field>
@ -63,6 +87,7 @@
<span @click="openDeleteIdentityConfirmation()">{{ $t("Delete this identity") }}</span> <span @click="openDeleteIdentityConfirmation()">{{ $t("Delete this identity") }}</span>
</div> </div>
</div> </div>
</div>
</template> </template>
<style scoped type="scss"> <style scoped type="scss">
@ -148,6 +173,8 @@ export default class EditIdentity extends mixins(identityEditionMixin) {
private currentActor: IPerson | null = null; private currentActor: IPerson | null = null;
RouteName = RouteName;
get message() { get message() {
if (this.isUpdate) return null; if (this.isUpdate) return null;
return this.$t("Only alphanumeric characters and underscores are supported."); return this.$t("Only alphanumeric characters and underscores are supported.");

View file

@ -1,4 +1,15 @@
<template> <template>
<div>
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li>
<router-link :to="{ name: RouteName.ADMIN }">{{ $t("Admin") }}</router-link>
</li>
<li class="is-active">
<router-link :to="{ name: RouteName.ADMIN_DASHBOARD }">{{ $t("Dashboard") }}</router-link>
</li>
</ul>
</nav>
<section> <section>
<h1 class="title">{{ $t("Administration") }}</h1> <h1 class="title">{{ $t("Administration") }}</h1>
<div class="tile is-ancestor" v-if="dashboard"> <div class="tile is-ancestor" v-if="dashboard">
@ -48,6 +59,7 @@
</div> </div>
</div> </div>
</section> </section>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue } from "vue-property-decorator"; import { Component, Vue } from "vue-property-decorator";

View file

@ -1,4 +1,23 @@
<template> <template>
<div>
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li>
<router-link :to="{ name: RouteName.ADMIN }">{{ $t("Admin") }}</router-link>
</li>
<li>
<router-link :to="{ name: RouteName.RELAYS }">{{ $t("Federation") }}</router-link>
</li>
<li class="is-active" v-if="$route.name == RouteName.RELAY_FOLLOWINGS">
<router-link :to="{ name: RouteName.RELAY_FOLLOWINGS }">{{
$t("Followings")
}}</router-link>
</li>
<li class="is-active" v-if="$route.name == RouteName.RELAY_FOLLOWERS">
<router-link :to="{ name: RouteName.RELAY_FOLLOWERS }">{{ $t("Followers") }}</router-link>
</li>
</ul>
</nav>
<section> <section>
<h1 class="title">{{ $t("Instances") }}</h1> <h1 class="title">{{ $t("Instances") }}</h1>
<div class="tabs is-boxed"> <div class="tabs is-boxed">
@ -35,6 +54,7 @@
</div> </div>
<router-view></router-view> <router-view></router-view>
</section> </section>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">

View file

@ -1,4 +1,15 @@
<template> <template>
<div>
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li>
<router-link :to="{ name: RouteName.MODERATION }">{{ $t("Moderation") }}</router-link>
</li>
<li class="is-active">
<router-link :to="{ name: RouteName.PROFILES }">{{ $t("Profiles") }}</router-link>
</li>
</ul>
</nav>
<div v-if="persons"> <div v-if="persons">
<b-switch v-model="local">{{ $t("Local") }}</b-switch> <b-switch v-model="local">{{ $t("Local") }}</b-switch>
<b-switch v-model="suspended">{{ $t("Suspended") }}</b-switch> <b-switch v-model="suspended">{{ $t("Suspended") }}</b-switch>
@ -65,6 +76,7 @@
</template> </template>
</b-table> </b-table>
</div> </div>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue, Watch } from "vue-property-decorator"; import { Component, Vue, Watch } from "vue-property-decorator";

View file

@ -1,4 +1,17 @@
<template> <template>
<div>
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li>
<router-link :to="{ name: RouteName.ADMIN }">{{ $t("Admin") }}</router-link>
</li>
<li class="is-active">
<router-link :to="{ name: RouteName.ADMIN_SETTINGS }">{{
$t("Instance settings")
}}</router-link>
</li>
</ul>
</nav>
<section v-if="adminSettings"> <section v-if="adminSettings">
<form @submit.prevent="updateSettings"> <form @submit.prevent="updateSettings">
<b-field :label="$t('Instance Name')"> <b-field :label="$t('Instance Name')">
@ -44,7 +57,9 @@
<div class="field"> <div class="field">
<label class="label has-help">{{ $t("Instance Rules") }}</label> <label class="label has-help">{{ $t("Instance Rules") }}</label>
<small> <small>
{{ $t("A place for your code of conduct, rules or guidelines. You can use HTML tags.") }} {{
$t("A place for your code of conduct, rules or guidelines. You can use HTML tags.")
}}
</small> </small>
<b-input type="textarea" v-model="adminSettings.instanceRules" /> <b-input type="textarea" v-model="adminSettings.instanceRules" />
</div> </div>
@ -196,7 +211,9 @@
v-if="adminSettings.instancePrivacyPolicyType === InstancePrivacyType.URL" v-if="adminSettings.instancePrivacyPolicyType === InstancePrivacyType.URL"
> >
<b>{{ $t("URL") }}</b> <b>{{ $t("URL") }}</b>
<p class="content">{{ $t("Set an URL to a page with your own privacy policy.") }}</p> <p class="content">
{{ $t("Set an URL to a page with your own privacy policy.") }}
</p>
</div> </div>
<div <div
class="notification" class="notification"
@ -236,6 +253,7 @@
<b-button native-type="submit" type="is-primary">{{ $t("Save") }}</b-button> <b-button native-type="submit" type="is-primary">{{ $t("Save") }}</b-button>
</form> </form>
</section> </section>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue } from "vue-property-decorator"; import { Component, Vue } from "vue-property-decorator";

View file

@ -1,4 +1,15 @@
<template> <template>
<div>
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li>
<router-link :to="{ name: RouteName.MODERATION }">{{ $t("Moderation") }}</router-link>
</li>
<li class="is-active">
<router-link :to="{ name: RouteName.USERS }">{{ $t("Users") }}</router-link>
</li>
</ul>
</nav>
<div v-if="users"> <div v-if="users">
<b-table <b-table
:data="users.elements" :data="users.elements"
@ -73,6 +84,7 @@
</template> </template>
</b-table> </b-table>
</div> </div>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue } from "vue-property-decorator"; import { Component, Vue } from "vue-property-decorator";

View file

@ -1,4 +1,20 @@
<template> <template>
<div>
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li>
<router-link :to="{ name: RouteName.GROUP }">{{ group.name }}</router-link>
</li>
<li>
<router-link :to="{ name: RouteName.GROUP_SETTINGS }">{{ $t("Settings") }}</router-link>
</li>
<li class="is-active">
<router-link :to="{ name: RouteName.GROUP_MEMBERS_SETTINGS }">{{
$t("Members")
}}</router-link>
</li>
</ul>
</nav>
<section class="container section" v-if="group"> <section class="container section" v-if="group">
<form @submit.prevent="inviteMember"> <form @submit.prevent="inviteMember">
<b-field :label="$t('Invite a new member')" custom-class="add-relay" horizontal> <b-field :label="$t('Invite a new member')" custom-class="add-relay" horizontal>
@ -15,6 +31,7 @@
<h1>{{ $t("Group Members") }} ({{ group.members.total }})</h1> <h1>{{ $t("Group Members") }} ({{ group.members.total }})</h1>
<pre>{{ group.members }}</pre> <pre>{{ group.members }}</pre>
</section> </section>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">

View file

@ -2,19 +2,21 @@
<aside class="section container"> <aside class="section container">
<h1 class="title">{{ $t("Settings") }}</h1> <h1 class="title">{{ $t("Settings") }}</h1>
<div class="columns"> <div class="columns">
<SettingsMenu class="column is-one-quarter-desktop" :menu="menu" /> <aside class="column is-one-quarter-desktop">
<div class="column">
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul> <ul>
<li <SettingMenuSection :title="$t('Settings')" :to="{ name: RouteName.GROUP_SETTINGS }">
v-for="route in routes.get($route.name)" <SettingMenuItem
:class="{ 'is-active': route.to.name === $route.name }" :title="this.$t('Public')"
:key="route.title" :to="{ name: RouteName.GROUP_PUBLIC_SETTINGS }"
> />
<router-link :to="{ name: route.to.name }">{{ route.title }}</router-link> <SettingMenuItem
</li> :title="this.$t('Members')"
:to="{ name: RouteName.GROUP_MEMBERS_SETTINGS }"
/>
</SettingMenuSection>
</ul> </ul>
</nav> </aside>
<div class="column">
<router-view /> <router-view />
</div> </div>
</div> </div>
@ -22,70 +24,20 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue, Watch } from "vue-property-decorator"; import { Component, Vue, Watch } from "vue-property-decorator";
import SettingsMenu from "@/components/Settings/SettingsMenu.vue";
import { ISettingMenuSection } from "@/types/setting-menu.model";
import { Route } from "vue-router"; import { Route } from "vue-router";
import { IGroup, IPerson } from "@/types/actor"; import { IGroup, IPerson } from "@/types/actor";
import { FETCH_GROUP } from "@/graphql/actor"; import { FETCH_GROUP } from "@/graphql/actor";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
import SettingMenuSection from "../../components/Settings/SettingMenuSection.vue";
import SettingMenuItem from "../../components/Settings/SettingMenuItem.vue";
@Component({ @Component({
components: { SettingsMenu }, components: { SettingMenuSection, SettingMenuItem },
apollo: {
group: {
query: FETCH_GROUP,
},
},
}) })
export default class Settings extends Vue { export default class Settings extends Vue {
RouteName = RouteName; RouteName = RouteName;
menu: ISettingMenuSection[] = [];
group!: IGroup[]; group!: IGroup[];
mounted() {
this.menu = [
{
title: this.$t("Settings") as string,
to: { name: RouteName.GROUP_SETTINGS } as Route,
items: [
{
title: this.$t("Public") as string,
to: { name: RouteName.GROUP_PUBLIC_SETTINGS } as Route,
},
{
title: this.$t("Members") as string,
to: { name: RouteName.GROUP_MEMBERS_SETTINGS } as Route,
},
],
},
];
}
get routes(): Map<string, Route[]> {
return this.getPath(this.menu);
}
getPath(object: ISettingMenuSection[]) {
function iter(menu: ISettingMenuSection[] | ISettingMenuSection, acc: ISettingMenuSection[]) {
if (Array.isArray(menu)) {
return menu.forEach((item: ISettingMenuSection) => {
iter(item, acc.concat(item));
});
}
if (menu.items && menu.items.length > 0) {
return menu.items.forEach((item: ISettingMenuSection) => {
iter(item, acc.concat(item));
});
}
result.set(menu.to.name, acc);
}
const result = new Map();
iter(object, []);
return result;
}
} }
</script> </script>

View file

@ -1,4 +1,17 @@
<template> <template>
<div>
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li>
<router-link :to="{ name: RouteName.MODERATION }">{{ $t("Moderation") }}</router-link>
</li>
<li class="is-active">
<router-link :to="{ name: RouteName.REPORT_LOGS }">{{
$t("Moderation log")
}}</router-link>
</li>
</ul>
</nav>
<section> <section>
<ul v-if="actionLogs.length > 0"> <ul v-if="actionLogs.length > 0">
<li v-for="log in actionLogs" :key="log.id"> <li v-for="log in actionLogs" :key="log.id">
@ -141,6 +154,7 @@
<b-message type="is-info">{{ $t("No moderation logs yet") }}</b-message> <b-message type="is-info">{{ $t("No moderation logs yet") }}</b-message>
</div> </div>
</section> </section>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue } from "vue-property-decorator"; import { Component, Vue } from "vue-property-decorator";

View file

@ -1,4 +1,20 @@
<template> <template>
<div>
<nav class="breadcrumb" aria-label="breadcrumbs" v-if="report">
<ul>
<li>
<router-link :to="{ name: RouteName.MODERATION }">{{ $t("Moderation") }}</router-link>
</li>
<li>
<router-link :to="{ name: RouteName.REPORTS }">{{ $t("Reports") }}</router-link>
</li>
<li class="is-active">
<router-link :to="{ name: RouteName.REPORT, params: { id: report.id } }">{{
$t("Report #{reportNumber}", { reportNumber: report.id })
}}</router-link>
</li>
</ul>
</nav>
<section> <section>
<b-message title="Error" type="is-danger" v-for="error in errors" :key="error"> <b-message title="Error" type="is-danger" v-for="error in errors" :key="error">
{{ error }} {{ error }}
@ -197,6 +213,7 @@
</form> </form>
</div> </div>
</section> </section>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"; import { Component, Prop, Vue } from "vue-property-decorator";

View file

@ -1,4 +1,15 @@
<template> <template>
<div>
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li>
<router-link :to="{ name: RouteName.MODERATION }">{{ $t("Moderation") }}</router-link>
</li>
<li class="is-active">
<router-link :to="{ name: RouteName.REPORTS }">{{ $t("Reports") }}</router-link>
</li>
</ul>
</nav>
<section> <section>
<b-field> <b-field>
<b-radio-button v-model="filterReports" :native-value="ReportStatusEnum.OPEN">{{ <b-radio-button v-model="filterReports" :native-value="ReportStatusEnum.OPEN">{{
@ -30,6 +41,7 @@
</b-message> </b-message>
</div> </div>
</section> </section>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue, Watch } from "vue-property-decorator"; import { Component, Prop, Vue, Watch } from "vue-property-decorator";

View file

@ -2,19 +2,8 @@
<div class="section container"> <div class="section container">
<h1 class="title">{{ $t("Settings") }}</h1> <h1 class="title">{{ $t("Settings") }}</h1>
<div class="columns"> <div class="columns">
<SettingsMenu class="column is-one-quarter-desktop" :menu="menu" /> <SettingsMenu class="column is-one-quarter-desktop" />
<div class="column"> <div class="column">
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li
v-for="route in routes.get($route.name)"
:class="{ 'is-active': route.to.name === $route.name }"
:key="route.to.name"
>
<router-link :to="{ name: route.to.name }">{{ route.title }}</router-link>
</li>
</ul>
</nav>
<router-view /> <router-view />
</div> </div>
</div> </div>
@ -25,7 +14,6 @@ import { Component, Vue, Watch } from "vue-property-decorator";
import { Route } from "vue-router"; import { Route } from "vue-router";
import SettingsMenu from "../components/Settings/SettingsMenu.vue"; import SettingsMenu from "../components/Settings/SettingsMenu.vue";
import RouteName from "../router/name"; import RouteName from "../router/name";
import { ISettingMenuSection } from "../types/setting-menu.model";
import { IPerson, Person } from "../types/actor"; import { IPerson, Person } from "../types/actor";
import { IDENTITIES } from "../graphql/actor"; import { IDENTITIES } from "../graphql/actor";
import { CURRENT_USER_CLIENT } from "../graphql/user"; import { CURRENT_USER_CLIENT } from "../graphql/user";
@ -44,148 +32,9 @@ import { ICurrentUser, ICurrentUserRole } from "../types/current-user.model";
export default class Settings extends Vue { export default class Settings extends Vue {
RouteName = RouteName; RouteName = RouteName;
menu: ISettingMenuSection[] = [];
identities!: IPerson[]; identities!: IPerson[];
newIdentity!: ISettingMenuSection;
currentUser!: ICurrentUser; currentUser!: ICurrentUser;
mounted() {
this.newIdentity = {
title: this.$t("New profile") as string,
to: { name: RouteName.CREATE_IDENTITY } as Route,
};
this.menu = [
{
title: this.$t("Account") as string,
to: { name: RouteName.ACCOUNT_SETTINGS } as Route,
items: [
{
title: this.$t("General") as string,
to: { name: RouteName.ACCOUNT_SETTINGS_GENERAL } as Route,
},
{
title: this.$t("Preferences") as string,
to: { name: RouteName.PREFERENCES } as Route,
},
{
title: this.$t("Email notifications") as string,
to: { name: RouteName.NOTIFICATIONS } as Route,
},
],
},
{
title: this.$t("Profiles") as string,
to: { name: RouteName.IDENTITIES } as Route,
items: [this.newIdentity],
},
];
if (
[ICurrentUserRole.MODERATOR, ICurrentUserRole.ADMINISTRATOR].includes(this.currentUser.role)
) {
this.menu.push({
title: this.$t("Moderation") as string,
to: { name: RouteName.MODERATION } as Route,
items: [
{
title: this.$t("Reports") as string,
to: { name: RouteName.REPORTS } as Route,
items: [
{
title: this.$t("Report") as string,
to: { name: RouteName.REPORT } as Route,
},
],
},
{
title: this.$t("Moderation log") as string,
to: { name: RouteName.REPORT_LOGS } as Route,
},
{
title: this.$t("Users") as string,
to: { name: RouteName.USERS } as Route,
},
{
title: this.$t("Profiles") as string,
to: { name: RouteName.PROFILES } as Route,
},
],
});
}
if (this.currentUser.role === ICurrentUserRole.ADMINISTRATOR) {
this.menu.push({
title: this.$t("Admin") as string,
to: { name: RouteName.ADMIN } as Route,
items: [
{
title: this.$t("Dashboard") as string,
to: { name: RouteName.ADMIN_DASHBOARD } as Route,
},
{
title: this.$t("Instance settings") as string,
to: { name: RouteName.ADMIN_SETTINGS } as Route,
},
{
title: this.$t("Federation") as string,
to: { name: RouteName.RELAYS } as Route,
items: [
{
title: this.$t("Followings") as string,
to: { name: RouteName.RELAY_FOLLOWINGS } as Route,
},
{
title: this.$t("Followers") as string,
to: { name: RouteName.RELAY_FOLLOWERS } as Route,
},
],
},
],
});
}
}
@Watch("identities")
updateIdentities(identities: IPerson[]) {
if (!identities) return;
if (!this.menu[1].items) return;
this.menu[1].items = [];
this.menu[1].items.push(
...identities.map((identity: IPerson) => ({
to: ({
name: RouteName.UPDATE_IDENTITY,
params: { identityName: identity.preferredUsername },
} as unknown) as Route,
title: `@${identity.preferredUsername}`,
}))
);
this.menu[1].items.push(this.newIdentity);
}
get routes(): Map<string, Route[]> {
return this.getPath(this.menu);
}
getPath(object: ISettingMenuSection[]) {
function iter(menu: ISettingMenuSection[] | ISettingMenuSection, acc: ISettingMenuSection[]) {
if (Array.isArray(menu)) {
return menu.forEach((item: ISettingMenuSection) => {
iter(item, acc.concat(item));
});
}
if (menu.items && menu.items.length > 0) {
return menu.items.forEach((item: ISettingMenuSection) => {
iter(item, acc.concat(item));
});
}
result.set(menu.to.name, acc);
}
const result = new Map();
iter(object, []);
return result;
}
} }
</script> </script>

View file

@ -1,4 +1,17 @@
<template> <template>
<div>
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li>
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS }">{{ $t("Account") }}</router-link>
</li>
<li class="is-active">
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS_GENERAL }">{{
$t("General")
}}</router-link>
</li>
</ul>
</nav>
<section> <section>
<div class="setting-title"> <div class="setting-title">
<h2>{{ $t("Email") }}</h2> <h2>{{ $t("Email") }}</h2>
@ -140,6 +153,7 @@
</section> </section>
</b-modal> </b-modal>
</section> </section>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">

View file

@ -1,5 +1,17 @@
<template> <template>
<div v-if="loggedUser"> <div v-if="loggedUser">
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li>
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS }">{{ $t("Account") }}</router-link>
</li>
<li class="is-active">
<router-link :to="{ name: RouteName.NOTIFICATIONS }">{{
$t("Email notifications")
}}</router-link>
</li>
</ul>
</nav>
<section> <section>
<div class="setting-title"> <div class="setting-title">
<h2>{{ $t("Participation notifications") }}</h2> <h2>{{ $t("Participation notifications") }}</h2>

View file

@ -1,4 +1,15 @@
<template> <template>
<div>
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li>
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS }">{{ $t("Account") }}</router-link>
</li>
<li class="is-active">
<router-link :to="{ name: RouteName.PREFERENCES }">{{ $t("Preferences") }}</router-link>
</li>
</ul>
</nav>
<div> <div>
<b-field :label="$t('Language')"> <b-field :label="$t('Language')">
<b-select <b-select
@ -34,6 +45,7 @@
}) })
}}</em> }}</em>
</div> </div>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue, Watch } from "vue-property-decorator"; import { Component, Vue, Watch } from "vue-property-decorator";
@ -42,6 +54,7 @@ import { USER_SETTINGS, SET_USER_SETTINGS, UPDATE_USER_LOCALE } from "../../grap
import { IConfig } from "../../types/config.model"; import { IConfig } from "../../types/config.model";
import { ICurrentUser } from "../../types/current-user.model"; import { ICurrentUser } from "../../types/current-user.model";
import langs from "../../i18n/langs.json"; import langs from "../../i18n/langs.json";
import RouteName from "../../router/name";
@Component({ @Component({
apollo: { apollo: {
@ -58,6 +71,8 @@ export default class Preferences extends Vue {
locale: string | null = null; locale: string | null = null;
RouteName = RouteName;
@Watch("loggedUser") @Watch("loggedUser")
setSavedTimezone(loggedUser: ICurrentUser) { setSavedTimezone(loggedUser: ICurrentUser) {
if (loggedUser && loggedUser.settings.timezone) { if (loggedUser && loggedUser.settings.timezone) {