Add a breadcrumbs component

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2022-01-10 15:19:16 +01:00 committed by tykayn
parent deda6da6f5
commit 759ebe2298
35 changed files with 695 additions and 830 deletions

View File

@ -0,0 +1,69 @@
<template>
<nav class="flex mb-3" :aria-label="$t('Breadcrumbs')">
<ol class="inline-flex items-center space-x-1 md:space-x-3">
<li
class="inline-flex items-center"
v-for="(element, index) in links"
:key="index"
:aria-current="index > 0 ? 'page' : undefined"
>
<router-link
v-if="index === 0"
:to="element"
class="inline-flex items-center text-gray-700 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"
>
{{ element.text }}
</router-link>
<div class="flex items-center" v-else-if="index === links.length - 1">
<svg
class="w-6 h-6 text-gray-400 rtl:rotate-180"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
></path>
</svg>
<span
class="ltr:ml-1 rtl:mr-1 font-medium text-gray-400 md:ltr:ml-2 md:rtl:mr-2 dark:text-gray-500"
>{{ element.text }}</span
>
</div>
<div class="flex items-center" v-else>
<svg
class="w-6 h-6 text-gray-400 rtl:rotate-180"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
></path>
</svg>
<router-link
:to="element"
class="ltr:ml-1 rtl:mr-1 font-medium text-gray-700 hover:text-gray-900 md:ltr:ml-2 md:rtl:mr-2 dark:text-gray-400 dark:hover:text-white"
>{{ element.text }}</router-link
>
</div>
</li>
<slot></slot>
</ol>
</nav>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
import { Location } from "vue-router";
type LinkElement = Location & { text: string };
@Component
export default class Breadcrumbs extends Vue {
@Prop({ type: Array, required: true }) links!: LinkElement[];
}
</script>

View File

@ -12,6 +12,7 @@ import { NotifierPlugin } from "./plugins/notifier";
import filters from "./filters"; import filters from "./filters";
import { i18n } from "./utils/i18n"; import { i18n } from "./utils/i18n";
import apolloProvider from "./vue-apollo"; import apolloProvider from "./vue-apollo";
import Breadcrumbs from "@/components/Utils/Breadcrumbs.vue";
import "./registerServiceWorker"; import "./registerServiceWorker";
import "./assets/tailwind.css"; import "./assets/tailwind.css";
@ -25,6 +26,7 @@ Vue.use(VueScrollTo);
Vue.use(VTooltip); Vue.use(VTooltip);
Vue.use(VueAnnouncer); Vue.use(VueAnnouncer);
Vue.use(VueSkipTo); Vue.use(VueSkipTo);
Vue.component("breadcrumbs-nav", Breadcrumbs);
// Register the router hooks with their names // Register the router hooks with their names
Component.registerHooks([ Component.registerHooks([

View File

@ -1,28 +1,6 @@
<template> <template>
<div> <div>
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav :links="breadcrumbsLinks" />
<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>
@ -253,6 +231,7 @@ import { ServerParseError } from "@apollo/client/link/http";
import { ApolloCache, FetchResult, InMemoryCache } from "@apollo/client/core"; import { ApolloCache, FetchResult, InMemoryCache } from "@apollo/client/core";
import pick from "lodash/pick"; import pick from "lodash/pick";
import { ActorType } from "@/types/enums"; import { ActorType } from "@/types/enums";
import { Location } from "vue-router";
@Component({ @Component({
components: { components: {
@ -670,5 +649,29 @@ export default class EditIdentity extends mixins(identityEditionMixin) {
this.oldDisplayName = null; this.oldDisplayName = null;
this.avatarFile = null; this.avatarFile = null;
} }
get breadcrumbsLinks(): (Location & { text: string })[] {
const links = [
{
name: RouteName.IDENTITIES,
params: {},
text: this.$t("Profiles") as string,
},
];
if (this.isUpdate && this.identity) {
links.push({
name: RouteName.UPDATE_IDENTITY,
params: { identityName: this.identity.preferredUsername },
text: this.identity.name,
});
} else {
links.push({
name: RouteName.CREATE_IDENTITY,
params: {},
text: this.$t("New profile") as string,
});
}
return links;
}
} }
</script> </script>

View File

@ -1,31 +1,19 @@
<template> <template>
<div v-if="group" class="section"> <div v-if="group" class="section">
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> { name: RouteName.ADMIN, text: $t('Admin') },
<router-link :to="{ name: RouteName.ADMIN }">{{ {
$t("Admin") name: RouteName.ADMIN_GROUPS,
}}</router-link> text: $t('Groups'),
</li> },
<li> {
<router-link name: RouteName.PROFILES,
:to="{ params: { id: group.id },
name: RouteName.ADMIN_GROUPS, text: displayName(group),
}" },
>{{ $t("Groups") }}</router-link ]"
> />
</li>
<li class="is-active">
<router-link
:to="{
name: RouteName.PROFILES,
params: { id: group.id },
}"
>{{ group.name || usernameWithDomain(group) }}</router-link
>
</li>
</ul>
</nav>
<div class="actor-card"> <div class="actor-card">
<p v-if="group.suspended"> <p v-if="group.suspended">
<actor-card <actor-card
@ -305,7 +293,11 @@ import { formatBytes } from "@/utils/datetime";
import { MemberRole } from "@/types/enums"; import { MemberRole } from "@/types/enums";
import { SUSPEND_PROFILE, UNSUSPEND_PROFILE } from "../../graphql/actor"; import { SUSPEND_PROFILE, UNSUSPEND_PROFILE } from "../../graphql/actor";
import { IGroup } from "../../types/actor"; import { IGroup } from "../../types/actor";
import { usernameWithDomain, IActor } from "../../types/actor/actor.model"; import {
usernameWithDomain,
displayName,
IActor,
} from "../../types/actor/actor.model";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
import ActorCard from "../../components/Account/ActorCard.vue"; import ActorCard from "../../components/Account/ActorCard.vue";
import EmptyContent from "../../components/Utils/EmptyContent.vue"; import EmptyContent from "../../components/Utils/EmptyContent.vue";
@ -359,6 +351,8 @@ export default class AdminGroupProfile extends Vue {
usernameWithDomain = usernameWithDomain; usernameWithDomain = usernameWithDomain;
displayName = displayName;
RouteName = RouteName; RouteName = RouteName;
EVENTS_PER_PAGE = EVENTS_PER_PAGE; EVENTS_PER_PAGE = EVENTS_PER_PAGE;

View File

@ -1,31 +1,20 @@
<template> <template>
<div v-if="person" class="section"> <div v-if="person" class="section">
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> { name: RouteName.ADMIN, text: $t('Admin') },
<router-link :to="{ name: RouteName.ADMIN }">{{ {
$t("Admin") name: RouteName.PROFILES,
}}</router-link> text: $t('Profiles'),
</li> },
<li> {
<router-link name: RouteName.PROFILES,
:to="{ params: { id: person.id },
name: RouteName.PROFILES, text: displayName(person),
}" },
>{{ $t("Profiles") }}</router-link ]"
> />
</li>
<li class="is-active">
<router-link
:to="{
name: RouteName.PROFILES,
params: { id: person.id },
}"
>{{ person.name || person.preferredUsername }}</router-link
>
</li>
</ul>
</nav>
<div class="actor-card"> <div class="actor-card">
<actor-card <actor-card
:actor="person" :actor="person"
@ -279,7 +268,7 @@ import {
UNSUSPEND_PROFILE, UNSUSPEND_PROFILE,
} from "../../graphql/actor"; } from "../../graphql/actor";
import { IPerson } from "../../types/actor"; import { IPerson } from "../../types/actor";
import { usernameWithDomain } from "../../types/actor/actor.model"; import { displayName, usernameWithDomain } from "../../types/actor/actor.model";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
import ActorCard from "../../components/Account/ActorCard.vue"; import ActorCard from "../../components/Account/ActorCard.vue";
import EmptyContent from "../../components/Utils/EmptyContent.vue"; import EmptyContent from "../../components/Utils/EmptyContent.vue";
@ -334,6 +323,8 @@ export default class AdminProfile extends Vue {
usernameWithDomain = usernameWithDomain; usernameWithDomain = usernameWithDomain;
displayName = displayName;
RouteName = RouteName; RouteName = RouteName;
EVENTS_PER_PAGE = EVENTS_PER_PAGE; EVENTS_PER_PAGE = EVENTS_PER_PAGE;

View File

@ -1,31 +1,20 @@
<template> <template>
<div v-if="user" class="section"> <div v-if="user" class="section">
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> { name: RouteName.ADMIN, text: $t('Admin') },
<router-link :to="{ name: RouteName.ADMIN }">{{ {
$t("Admin") name: RouteName.USERS,
}}</router-link> text: $t('Users'),
</li> },
<li> {
<router-link name: RouteName.ADMIN_USER_PROFILE,
:to="{ params: { id: user.id },
name: RouteName.USERS, text: user.email,
}" },
>{{ $t("Users") }}</router-link ]"
> />
</li>
<li class="is-active">
<router-link
:to="{
name: RouteName.ADMIN_USER_PROFILE,
params: { id: user.id },
}"
>{{ user.email }}</router-link
>
</li>
</ul>
</nav>
<table v-if="metadata.length > 0" class="table is-fullwidth"> <table v-if="metadata.length > 0" class="table is-fullwidth">
<tbody> <tbody>
<tr v-for="{ key, value, link, elements, type } in metadata" :key="key"> <tr v-for="{ key, value, link, elements, type } in metadata" :key="key">

View File

@ -1,19 +1,11 @@
<template> <template>
<div> <div>
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> { name: RouteName.ADMIN, text: $t('Admin') },
<router-link :to="{ name: RouteName.ADMIN }">{{ { text: $t('Dashboard') },
$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">

View File

@ -1,19 +1,14 @@
<template> <template>
<div> <div>
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> { name: RouteName.MODERATION, text: $t('Moderation') },
<router-link :to="{ name: RouteName.MODERATION }">{{ {
$t("Moderation") name: RouteName.ADMIN_GROUPS,
}}</router-link> text: $t('Groups'),
</li> },
<li class="is-active"> ]"
<router-link :to="{ name: RouteName.PROFILES }">{{ />
$t("Groups")
}}</router-link>
</li>
</ul>
</nav>
<div class="buttons" v-if="showCreateGroupsButton"> <div class="buttons" v-if="showCreateGroupsButton">
<router-link <router-link
class="button is-primary" class="button is-primary"

View File

@ -1,28 +1,12 @@
<template> <template>
<div v-if="instance"> <div v-if="instance">
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> { name: RouteName.ADMIN, text: $t('Admin') },
<router-link :to="{ name: RouteName.ADMIN }">{{ { name: RouteName.INSTANCES, text: $t('Instances') },
$t("Admin") { text: instance.domain },
}}</router-link> ]"
</li> />
<li>
<router-link :to="{ name: RouteName.INSTANCES }">{{
$t("Instances")
}}</router-link>
</li>
<li class="is-active">
<router-link
:to="{
name: RouteName.INSTANCE,
params: { domain: instance.domain },
}"
>{{ instance.domain }}</router-link
>
</li>
</ul>
</nav>
<h1 class="text-2xl">{{ instance.domain }}</h1> <h1 class="text-2xl">{{ instance.domain }}</h1>
<div class="grid md:grid-cols-4 gap-2 content-center text-center mt-2"> <div class="grid md:grid-cols-4 gap-2 content-center text-center mt-2">
<div class="bg-gray-50 rounded-xl p-8 dark:bg-gray-800"> <div class="bg-gray-50 rounded-xl p-8 dark:bg-gray-800">

View File

@ -1,28 +1,16 @@
<template> <template>
<div> <div>
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> { name: RouteName.ADMIN, text: $t('Admin') },
<router-link :to="{ name: RouteName.ADMIN }">{{ { text: $t('Instances') },
$t("Admin") ]"
}}</router-link> />
</li>
<li class="is-active">
<router-link :to="{ name: RouteName.INSTANCES }">{{
$t("Instances")
}}</router-link>
</li>
</ul>
</nav>
<section> <section>
<h1 class="title">{{ $t("Instances") }}</h1> <h1 class="title">{{ $t("Instances") }}</h1>
<form @submit="followInstance" class="my-4"> <form @submit="followInstance" class="my-4">
<b-field <b-field :label="$t('Follow a new instance')" horizontal>
:label="$t('Follow a new instance')" <b-field grouped group-multiline expanded size="is-large">
custom-class="add-relay"
horizontal
>
<b-field grouped expanded size="is-large">
<p class="control"> <p class="control">
<b-input <b-input
v-model="newRelayAddress" v-model="newRelayAddress"

View File

@ -1,19 +1,14 @@
<template> <template>
<div> <div>
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> { name: RouteName.MODERATION, text: $t('Moderation') },
<router-link :to="{ name: RouteName.MODERATION }">{{ {
$t("Moderation") name: RouteName.PROFILES,
}}</router-link> text: $t('Profiles'),
</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>

View File

@ -1,19 +1,12 @@
<template> <template>
<div> <div>
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> { name: RouteName.ADMIN, text: $t('Admin') },
<router-link :to="{ name: RouteName.ADMIN }">{{ { text: $t('Instance settings') },
$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="settingsToWrite"> <section v-if="settingsToWrite">
<form @submit.prevent="updateSettings"> <form @submit.prevent="updateSettings">
<b-field :label="$t('Instance Name')" label-for="instance-name"> <b-field :label="$t('Instance Name')" label-for="instance-name">

View File

@ -1,19 +1,14 @@
<template> <template>
<div> <div>
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> { name: RouteName.MODERATION, text: $t('Moderation') },
<router-link :to="{ name: RouteName.MODERATION }">{{ {
$t("Moderation") name: RouteName.USERS,
}}</router-link> text: $t('Users'),
</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"

View File

@ -1,42 +1,29 @@
<template> <template>
<section class="section container"> <section class="section container">
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul v-if="group"> v-if="group"
<li> :links="[
<router-link :to="{ name: RouteName.MY_GROUPS }">{{ {
$t("My groups") name: RouteName.MY_GROUPS,
}}</router-link> text: $t('My groups'),
</li> },
<li> {
<router-link name: RouteName.GROUP,
:to="{ params: { preferredUsername: usernameWithDomain(group) },
name: RouteName.GROUP, text: displayName(group),
params: { preferredUsername: usernameWithDomain(group) }, },
}" {
>{{ group.name }}</router-link name: RouteName.DISCUSSION_LIST,
> params: { preferredUsername: usernameWithDomain(group) },
</li> text: $t('Discussions'),
<li> },
<router-link {
:to="{ name: RouteName.CREATE_DISCUSSION,
name: RouteName.DISCUSSION_LIST, params: { preferredUsername: usernameWithDomain(group) },
params: { preferredUsername: usernameWithDomain(group) }, text: $t('Create'),
}" },
>{{ $t("Discussions") }}</router-link ]"
> />
</li>
<li class="is-active">
<router-link
:to="{
name: RouteName.CREATE_DISCUSSION,
params: { preferredUsername: usernameWithDomain(group) },
}"
>{{ $t("Create") }}</router-link
>
</li>
</ul>
<b-skeleton v-else-if="$apollo.loading" :animated="animated"></b-skeleton>
</nav>
<h1 class="title">{{ $t("Create a discussion") }}</h1> <h1 class="title">{{ $t("Create a discussion") }}</h1>
<form @submit.prevent="createDiscussion"> <form @submit.prevent="createDiscussion">
@ -67,7 +54,12 @@
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"; import { Component, Prop, Vue } from "vue-property-decorator";
import { IGroup, IPerson, usernameWithDomain } from "@/types/actor"; import {
displayName,
IGroup,
IPerson,
usernameWithDomain,
} from "@/types/actor";
import { CURRENT_ACTOR_CLIENT } from "@/graphql/actor"; import { CURRENT_ACTOR_CLIENT } from "@/graphql/actor";
import { FETCH_GROUP } from "@/graphql/group"; import { FETCH_GROUP } from "@/graphql/group";
import { CREATE_DISCUSSION } from "@/graphql/discussion"; import { CREATE_DISCUSSION } from "@/graphql/discussion";
@ -113,6 +105,8 @@ export default class CreateDiscussion extends Vue {
usernameWithDomain = usernameWithDomain; usernameWithDomain = usernameWithDomain;
displayName = displayName;
async createDiscussion(): Promise<void> { async createDiscussion(): Promise<void> {
this.errors = { title: "" }; this.errors = { title: "" };
try { try {

View File

@ -1,46 +1,29 @@
<template> <template>
<div class="container section" v-if="discussion"> <div class="container section" v-if="discussion">
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> v-if="group"
<li> :links="[
<router-link :to="{ name: RouteName.MY_GROUPS }">{{ {
$t("My groups") name: RouteName.MY_GROUPS,
}}</router-link> text: $t('My groups'),
</li> },
<li> {
<router-link name: RouteName.GROUP,
v-if="discussion.actor" params: { preferredUsername: usernameWithDomain(group) },
:to="{ text: displayName(group),
name: RouteName.GROUP, },
params: { {
preferredUsername: usernameWithDomain(discussion.actor), name: RouteName.DISCUSSION_LIST,
}, params: { preferredUsername: usernameWithDomain(group) },
}" text: $t('Discussions'),
>{{ discussion.actor.name }}</router-link },
> {
<b-skeleton v-else-if="$apollo.loading" animated /> name: RouteName.DISCUSSION,
</li> params: { id: discussion.id },
<li> text: discussion.title,
<router-link },
v-if="discussion.actor" ]"
:to="{ />
name: RouteName.DISCUSSION_LIST,
params: {
preferredUsername: usernameWithDomain(discussion.actor),
},
}"
>{{ $t("Discussions") }}</router-link
>
<b-skeleton animated v-else-if="$apollo.loading" />
</li>
<li class="is-active">
<router-link
:to="{ name: RouteName.DISCUSSION, params: { id: discussion.id } }"
>{{ discussion.title }}</router-link
>
</li>
</ul>
</nav>
<b-message v-if="error" type="is-danger"> <b-message v-if="error" type="is-danger">
{{ error }} {{ error }}
</b-message> </b-message>
@ -148,7 +131,7 @@ import {
} from "@/graphql/discussion"; } from "@/graphql/discussion";
import { IDiscussion } from "@/types/discussions"; import { IDiscussion } from "@/types/discussions";
import { Discussion as DiscussionModel } from "@/types/discussions"; import { Discussion as DiscussionModel } from "@/types/discussions";
import { usernameWithDomain } from "@/types/actor"; import { displayName, usernameWithDomain } from "@/types/actor";
import DiscussionComment from "@/components/Discussion/DiscussionComment.vue"; import DiscussionComment from "@/components/Discussion/DiscussionComment.vue";
import { GraphQLError } from "graphql"; import { GraphQLError } from "graphql";
import { DELETE_COMMENT, UPDATE_COMMENT } from "@/graphql/comment"; import { DELETE_COMMENT, UPDATE_COMMENT } from "@/graphql/comment";
@ -250,6 +233,7 @@ export default class Discussion extends mixins(GroupMixin) {
RouteName = RouteName; RouteName = RouteName;
usernameWithDomain = usernameWithDomain; usernameWithDomain = usernameWithDomain;
displayName = displayName;
error: string | null = null; error: string | null = null;
async reply(): Promise<void> { async reply(): Promise<void> {

View File

@ -1,32 +1,23 @@
<template> <template>
<div class="container section" v-if="group"> <div class="container section" v-if="group">
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> {
<router-link :to="{ name: RouteName.MY_GROUPS }">{{ name: RouteName.MY_GROUPS,
$t("My groups") text: $t('My groups'),
}}</router-link> },
</li> {
<li> name: RouteName.GROUP,
<router-link params: { preferredUsername: usernameWithDomain(group) },
:to="{ text: displayName(group),
name: RouteName.GROUP, },
params: { preferredUsername: usernameWithDomain(group) }, {
}" name: RouteName.DISCUSSION_LIST,
>{{ group.name }}</router-link params: { preferredUsername: usernameWithDomain(group) },
> text: $t('Discussions'),
</li> },
<li class="is-active"> ]"
<router-link />
:to="{
name: RouteName.DISCUSSION_LIST,
params: { preferredUsername: usernameWithDomain(group) },
}"
>{{ $t("Discussions") }}</router-link
>
</li>
</ul>
</nav>
<section v-if="isCurrentActorAGroupMember"> <section v-if="isCurrentActorAGroupMember">
<p> <p>
{{ {{
@ -82,7 +73,13 @@
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"; import { Component, Prop, Vue } from "vue-property-decorator";
import { FETCH_GROUP } from "@/graphql/group"; import { FETCH_GROUP } from "@/graphql/group";
import { IActor, IGroup, IPerson, usernameWithDomain } from "@/types/actor"; import {
displayName,
IActor,
IGroup,
IPerson,
usernameWithDomain,
} from "@/types/actor";
import DiscussionListItem from "@/components/Discussion/DiscussionListItem.vue"; import DiscussionListItem from "@/components/Discussion/DiscussionListItem.vue";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
import { MemberRole } from "@/types/enums"; import { MemberRole } from "@/types/enums";
@ -166,6 +163,7 @@ export default class DiscussionsList extends Vue {
RouteName = RouteName; RouteName = RouteName;
usernameWithDomain = usernameWithDomain; usernameWithDomain = usernameWithDomain;
displayName = displayName;
DISCUSSIONS_PER_PAGE = DISCUSSIONS_PER_PAGE; DISCUSSIONS_PER_PAGE = DISCUSSIONS_PER_PAGE;

View File

@ -1,27 +1,19 @@
<template> <template>
<div class="container section" v-if="group"> <div class="container section" v-if="group">
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> {
<router-link name: RouteName.GROUP,
:to="{ params: { preferredUsername: usernameWithDomain(group) },
name: RouteName.GROUP, text: displayName(group),
params: { preferredUsername: usernameWithDomain(group) }, },
}" {
>{{ group.name }}</router-link name: RouteName.EVENTS,
> params: { preferredUsername: usernameWithDomain(group) },
</li> text: $t('Events'),
<li class="is-active"> },
<router-link ]"
:to="{ />
name: RouteName.TODO_LISTS,
params: { preferredUsername: usernameWithDomain(group) },
}"
>{{ $t("Events") }}</router-link
>
</li>
</ul>
</nav>
<section> <section>
<h1 class="title" v-if="group"> <h1 class="title" v-if="group">
{{ {{
@ -89,7 +81,7 @@ import { PERSON_MEMBERSHIPS } from "@/graphql/actor";
import GroupMixin from "@/mixins/group"; import GroupMixin from "@/mixins/group";
import { IMember } from "@/types/actor/member.model"; import { IMember } from "@/types/actor/member.model";
import { FETCH_GROUP_EVENTS } from "@/graphql/event"; import { FETCH_GROUP_EVENTS } from "@/graphql/event";
import { usernameWithDomain } from "../../types/actor"; import { displayName, usernameWithDomain } from "../../types/actor";
const EVENTS_PAGE_LIMIT = 10; const EVENTS_PAGE_LIMIT = 10;
@ -143,6 +135,8 @@ export default class GroupEvents extends mixins(GroupMixin) {
usernameWithDomain = usernameWithDomain; usernameWithDomain = usernameWithDomain;
displayName = displayName;
RouteName = RouteName; RouteName = RouteName;
EVENTS_PAGE_LIMIT = EVENTS_PAGE_LIMIT; EVENTS_PAGE_LIMIT = EVENTS_PAGE_LIMIT;

View File

@ -1,32 +1,20 @@
<template> <template>
<section class="section container" v-if="event"> <section class="section container" v-if="event">
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> { name: RouteName.MY_EVENTS, text: $t('My events') },
<router-link :to="{ name: RouteName.MY_EVENTS }">{{ {
$t("My events") name: RouteName.EVENT,
}}</router-link> params: { uuid: event.uuid },
</li> text: event.title,
<li> },
<router-link {
:to="{ name: RouteName.PARTICIPANTS,
name: RouteName.EVENT, params: { uuid: event.uuid },
params: { uuid: event.uuid }, text: $t('Participants'),
}" },
>{{ event.title }}</router-link ]"
> />
</li>
<li class="is-active">
<router-link
:to="{
name: RouteName.PARTICIPANTS,
params: { uuid: event.uuid },
}"
>{{ $t("Participants") }}</router-link
>
</li>
</ul>
</nav>
<h1 class="title">{{ $t("Participants") }}</h1> <h1 class="title">{{ $t("Participants") }}</h1>
<div class="level"> <div class="level">
<div class="level-left"> <div class="level-left">

View File

@ -1,27 +1,17 @@
<template> <template>
<div class="container is-widescreen"> <div class="container is-widescreen">
<div class="header"> <div class="header">
<nav class="breadcrumb" :aria-label="$t('Breadcrumbs')"> <breadcrumbs-nav
<ul> v-if="group"
<li> :links="[
<router-link :to="{ name: RouteName.MY_GROUPS }">{{ { name: RouteName.MY_GROUPS, text: $t('My groups') },
$t("My groups") {
}}</router-link> name: RouteName.GROUP,
</li> params: { preferredUsername: usernameWithDomain(group) },
<li class="is-active"> text: displayName(group),
<router-link },
aria-current-value="location" ]"
v-if="group && group.preferredUsername" />
:to="{
name: RouteName.GROUP,
params: { preferredUsername: usernameWithDomain(group) },
}"
>{{ group.name }}</router-link
>
<b-skeleton v-else :animated="true"></b-skeleton>
</li>
</ul>
</nav>
<b-loading :active.sync="$apollo.loading"></b-loading> <b-loading :active.sync="$apollo.loading"></b-loading>
<header class="block-container presentation" v-if="group"> <header class="block-container presentation" v-if="group">
<div class="banner-container"> <div class="banner-container">
@ -776,6 +766,8 @@ export default class Group extends mixins(GroupMixin) {
usernameWithDomain = usernameWithDomain; usernameWithDomain = usernameWithDomain;
displayName = displayName;
PostVisibility = PostVisibility; PostVisibility = PostVisibility;
Openness = Openness; Openness = Openness;

View File

@ -1,36 +1,25 @@
<template> <template>
<div> <div>
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul v-if="group"> v-if="group"
<li> :links="[
<router-link {
:to="{ name: RouteName.GROUP,
name: RouteName.GROUP, params: { preferredUsername: usernameWithDomain(group) },
params: { preferredUsername: usernameWithDomain(group) }, text: displayName(group),
}" },
>{{ group.name }}</router-link {
> name: RouteName.GROUP_SETTINGS,
</li> params: { preferredUsername: usernameWithDomain(group) },
<li> text: $t('Settings'),
<router-link },
:to="{ {
name: RouteName.GROUP_SETTINGS, name: RouteName.GROUP_FOLLOWERS_SETTINGS,
params: { preferredUsername: usernameWithDomain(group) }, params: { preferredUsername: usernameWithDomain(group) },
}" text: $t('Followers'),
>{{ $t("Settings") }}</router-link },
> ]"
</li> />
<li class="is-active">
<router-link
:to="{
name: RouteName.GROUP_FOLLOWERS_SETTINGS,
params: { preferredUsername: usernameWithDomain(group) },
}"
>{{ $t("Followers") }}</router-link
>
</li>
</ul>
</nav>
<b-loading :active="$apollo.loading" /> <b-loading :active="$apollo.loading" />
<section <section
class="container section" class="container section"
@ -138,7 +127,7 @@ import GroupMixin from "@/mixins/group";
import { mixins } from "vue-class-component"; import { mixins } from "vue-class-component";
import { GROUP_FOLLOWERS, UPDATE_FOLLOWER } from "@/graphql/followers"; import { GROUP_FOLLOWERS, UPDATE_FOLLOWER } from "@/graphql/followers";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
import { usernameWithDomain } from "../../types/actor"; import { displayName, usernameWithDomain } from "../../types/actor";
import EmptyContent from "@/components/Utils/EmptyContent.vue"; import EmptyContent from "@/components/Utils/EmptyContent.vue";
import { IFollower } from "@/types/actor/follower.model"; import { IFollower } from "@/types/actor/follower.model";
import { Paginate } from "@/types/paginate"; import { Paginate } from "@/types/paginate";
@ -181,6 +170,8 @@ export default class GroupFollowers extends mixins(GroupMixin) {
usernameWithDomain = usernameWithDomain; usernameWithDomain = usernameWithDomain;
displayName = displayName;
followers!: Paginate<IFollower>; followers!: Paginate<IFollower>;
mounted(): void { mounted(): void {

View File

@ -1,36 +1,25 @@
<template> <template>
<div> <div>
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul v-if="group"> v-if="group"
<li> :links="[
<router-link {
:to="{ name: RouteName.GROUP,
name: RouteName.GROUP, params: { preferredUsername: usernameWithDomain(group) },
params: { preferredUsername: usernameWithDomain(group) }, text: displayName(group),
}" },
>{{ group.name }}</router-link {
> name: RouteName.GROUP_SETTINGS,
</li> params: { preferredUsername: usernameWithDomain(group) },
<li> text: $t('Settings'),
<router-link },
:to="{ {
name: RouteName.GROUP_SETTINGS, name: RouteName.GROUP_MEMBERS_SETTINGS,
params: { preferredUsername: usernameWithDomain(group) }, params: { preferredUsername: usernameWithDomain(group) },
}" text: $t('Members'),
>{{ $t("Settings") }}</router-link },
> ]"
</li> />
<li class="is-active">
<router-link
:to="{
name: RouteName.GROUP_MEMBERS_SETTINGS,
params: { preferredUsername: usernameWithDomain(group) },
}"
>{{ $t("Members") }}</router-link
>
</li>
</ul>
</nav>
<b-loading :active="$apollo.loading" /> <b-loading :active="$apollo.loading" />
<section <section
class="container section" class="container section"
@ -312,6 +301,8 @@ export default class GroupMembers extends mixins(GroupMixin) {
usernameWithDomain = usernameWithDomain; usernameWithDomain = usernameWithDomain;
displayName = displayName;
mounted(): void { mounted(): void {
const roleQuery = this.$route.query.role as string; const roleQuery = this.$route.query.role as string;
if (Object.values(MemberRole).includes(roleQuery as MemberRole)) { if (Object.values(MemberRole).includes(roleQuery as MemberRole)) {

View File

@ -1,37 +1,25 @@
<template> <template>
<div> <div>
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> v-if="group"
<li> :links="[
<router-link {
v-if="group" name: RouteName.GROUP,
:to="{ params: { preferredUsername: usernameWithDomain(group) },
name: RouteName.GROUP, text: displayName(group),
params: { preferredUsername: usernameWithDomain(group) }, },
}" {
>{{ group.name || usernameWithDomain(group) }}</router-link name: RouteName.GROUP_SETTINGS,
> params: { preferredUsername: usernameWithDomain(group) },
</li> text: $t('Settings'),
<li> },
<router-link {
:to="{ name: RouteName.GROUP_PUBLIC_SETTINGS,
name: RouteName.GROUP_SETTINGS, params: { preferredUsername: usernameWithDomain(group) },
params: { preferredUsername: usernameWithDomain(group) }, text: $t('Group settings'),
}" },
>{{ $t("Settings") }}</router-link ]"
> />
</li>
<li class="is-active">
<router-link
:to="{
name: RouteName.GROUP_PUBLIC_SETTINGS,
params: { preferredUsername: usernameWithDomain(group) },
}"
>{{ $t("Group settings") }}</router-link
>
</li>
</ul>
</nav>
<b-loading :active="$apollo.loading" /> <b-loading :active="$apollo.loading" />
<section <section
class="container section" class="container section"
@ -197,7 +185,12 @@ import { mixins } from "vue-class-component";
import GroupMixin from "@/mixins/group"; import GroupMixin from "@/mixins/group";
import { GroupVisibility, Openness } from "@/types/enums"; import { GroupVisibility, Openness } from "@/types/enums";
import { UPDATE_GROUP } from "../../graphql/group"; import { UPDATE_GROUP } from "../../graphql/group";
import { Group, IGroup, usernameWithDomain } from "../../types/actor"; import {
Group,
IGroup,
usernameWithDomain,
displayName,
} from "../../types/actor";
import { Address, IAddress } from "../../types/address.model"; import { Address, IAddress } from "../../types/address.model";
import { CONFIG } from "@/graphql/config"; import { CONFIG } from "@/graphql/config";
import { IConfig } from "@/types/config.model"; import { IConfig } from "@/types/config.model";
@ -234,6 +227,8 @@ export default class GroupSettings extends mixins(GroupMixin) {
usernameWithDomain = usernameWithDomain; usernameWithDomain = usernameWithDomain;
displayName = displayName;
GroupVisibility = GroupVisibility; GroupVisibility = GroupVisibility;
Openness = Openness; Openness = Openness;

View File

@ -1,27 +1,21 @@
<template> <template>
<div class="container section"> <div class="container section">
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul v-if="group"> v-if="group"
<li> :links="[
<router-link {
:to="{ name: RouteName.GROUP,
name: RouteName.GROUP, params: { preferredUsername: usernameWithDomain(group) },
params: { preferredUsername: usernameWithDomain(group) }, text: displayName(group),
}" },
>{{ group.name }}</router-link {
> name: RouteName.TIMELINE,
</li> params: { preferredUsername: usernameWithDomain(group) },
<li> text: $t('Activity'),
<router-link },
:to="{ ]"
name: RouteName.TIMELINE, />
params: { preferredUsername: usernameWithDomain(group) },
}"
>{{ $t("Activity") }}</router-link
>
</li>
</ul>
</nav>
<section class="timeline"> <section class="timeline">
<b-field> <b-field>
<b-radio-button v-model="activityType" :native-value="undefined"> <b-radio-button v-model="activityType" :native-value="undefined">
@ -160,7 +154,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { GROUP_TIMELINE } from "@/graphql/group"; import { GROUP_TIMELINE } from "@/graphql/group";
import { IGroup, usernameWithDomain } from "@/types/actor"; import { IGroup, usernameWithDomain, displayName } from "@/types/actor";
import { ActivityType } from "@/types/enums"; import { ActivityType } from "@/types/enums";
import { Paginate } from "@/types/paginate"; import { Paginate } from "@/types/paginate";
import { Component, Prop, Vue } from "vue-property-decorator"; import { Component, Prop, Vue } from "vue-property-decorator";
@ -234,6 +228,8 @@ export default class Timeline extends Vue {
usernameWithDomain = usernameWithDomain; usernameWithDomain = usernameWithDomain;
displayName = displayName;
ActivityType = ActivityType; ActivityType = ActivityType;
ActivityAuthorFilter = ActivityAuthorFilter; ActivityAuthorFilter = ActivityAuthorFilter;

View File

@ -1,19 +1,17 @@
<template> <template>
<div> <div>
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> {
<router-link :to="{ name: RouteName.MODERATION }">{{ name: RouteName.MODERATION,
$t("Moderation") text: $t('Moderation'),
}}</router-link> },
</li> {
<li class="is-active"> name: RouteName.REPORT_LOGS,
<router-link :to="{ name: RouteName.REPORT_LOGS }">{{ text: $t('Moderation log'),
$t("Moderation log") },
}}</router-link> ]"
</li> />
</ul>
</nav>
<section v-if="actionLogs.total > 0 && actionLogs.elements.length > 0"> <section v-if="actionLogs.total > 0 && actionLogs.elements.length > 0">
<ul> <ul>
<li v-for="log in actionLogs.elements" :key="log.id"> <li v-for="log in actionLogs.elements" :key="log.id">

View File

@ -1,27 +1,23 @@
<template> <template>
<div> <div>
<nav class="breadcrumb" aria-label="breadcrumbs" v-if="report"> <breadcrumbs-nav
<ul> v-if="report"
<li> :links="[
<router-link :to="{ name: RouteName.MODERATION }">{{ {
$t("Moderation") name: RouteName.MODERATION,
}}</router-link> text: $t('Moderation'),
</li> },
<li> {
<router-link :to="{ name: RouteName.REPORTS }">{{ name: RouteName.REPORTS,
$t("Reports") text: $t('Reports'),
}}</router-link> },
</li> {
<li class="is-active"> name: RouteName.REPORT,
<router-link params: { id: report.id },
:to="{ name: RouteName.REPORT, params: { id: report.id } }" text: $t('Report #{reportNumber}', { reportNumber: report.id }),
>{{ },
$t("Report #{reportNumber}", { reportNumber: report.id }) ]"
}}</router-link />
>
</li>
</ul>
</nav>
<section> <section>
<b-message <b-message
title="Error" title="Error"

View File

@ -1,19 +1,17 @@
<template> <template>
<div> <div>
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> {
<router-link :to="{ name: RouteName.MODERATION }">{{ name: RouteName.MODERATION,
$t("Moderation") text: $t('Moderation'),
}}</router-link> },
</li> {
<li class="is-active"> name: RouteName.REPORTS,
<router-link :to="{ name: RouteName.REPORTS }">{{ text: $t('Reports'),
$t("Reports") },
}}</router-link> ]"
</li> />
</ul>
</nav>
<section> <section>
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
<b-field :label="$t('Report status')"> <b-field :label="$t('Report status')">
@ -93,7 +91,7 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Ref, Vue } from "vue-property-decorator"; import { Component, Vue } from "vue-property-decorator";
import { IReport } from "@/types/report.model"; import { IReport } from "@/types/report.model";
import { REPORTS } from "@/graphql/report"; import { REPORTS } from "@/graphql/report";
import ReportCard from "@/components/Report/ReportCard.vue"; import ReportCard from "@/components/Report/ReportCard.vue";

View File

@ -2,43 +2,7 @@
<div> <div>
<form @submit.prevent="publish(false)" v-if="isCurrentActorAGroupModerator"> <form @submit.prevent="publish(false)" v-if="isCurrentActorAGroupModerator">
<div class="container section"> <div class="container section">
<nav class="breadcrumb" aria-label="breadcrumbs" v-if="actualGroup"> <breadcrumbs-nav v-if="actualGroup" :links="breadcrumbLinks" />
<ul>
<li>
<router-link
v-if="actualGroup"
:to="{
name: RouteName.GROUP,
params: {
preferredUsername: usernameWithDomain(actualGroup),
},
}"
>{{
actualGroup.name || actualGroup.preferredUsername
}}</router-link
>
<b-skeleton v-else :animated="true"></b-skeleton>
</li>
<li>
<router-link
v-if="actualGroup"
:to="{
name: RouteName.POSTS,
params: {
preferredUsername: usernameWithDomain(actualGroup),
},
}"
>{{ $t("Posts") }}</router-link
>
<b-skeleton v-else :animated="true"></b-skeleton>
</li>
<li class="is-active">
<span v-if="preferredUsername">{{ $t("New post") }}</span>
<span v-else-if="slug">{{ $t("Edit post") }}</span>
<b-skeleton v-else :animated="true"></b-skeleton>
</li>
</ul>
</nav>
<h1 class="title" v-if="isUpdate === true"> <h1 class="title" v-if="isUpdate === true">
{{ $t("Edit post") }} {{ $t("Edit post") }}
</h1> </h1>
@ -174,7 +138,7 @@ import { CREATE_POST, UPDATE_POST } from "../../graphql/post";
import { IPost } from "../../types/post.model"; import { IPost } from "../../types/post.model";
import Editor from "../../components/Editor.vue"; import Editor from "../../components/Editor.vue";
import { IActor, usernameWithDomain } from "../../types/actor"; import { displayName, IActor, usernameWithDomain } from "../../types/actor";
import TagInput from "../../components/Event/TagInput.vue"; import TagInput from "../../components/Event/TagInput.vue";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
import Subtitle from "../../components/Utils/Subtitle.vue"; import Subtitle from "../../components/Utils/Subtitle.vue";
@ -366,6 +330,39 @@ export default class EditPost extends mixins(GroupMixin, PostMixin) {
} }
return this.group; return this.group;
} }
get breadcrumbLinks() {
const links = [
{
name: RouteName.GROUP,
params: {
preferredUsername: usernameWithDomain(this.actualGroup),
},
text: displayName(this.actualGroup),
},
{
name: RouteName.POSTS,
params: {
preferredUsername: usernameWithDomain(this.actualGroup),
},
text: this.$t("Posts"),
},
];
if (this.preferredUsername) {
links.push({
text: this.$t("New post") as string,
name: RouteName.POST_EDIT,
params: { preferredUsername: usernameWithDomain(this.actualGroup) },
});
} else {
links.push({
text: this.$t("Edit post") as string,
name: RouteName.POST_EDIT,
params: { preferredUsername: usernameWithDomain(this.actualGroup) },
});
}
return links;
}
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -1,31 +1,19 @@
<template> <template>
<div class="container section" v-if="group"> <div class="container section" v-if="group">
<nav class="breadcrumb" aria-label="breadcrumbs" v-if="group"> <breadcrumbs-nav
<ul> :links="[
<li> {
<router-link name: RouteName.GROUP,
v-if="group" params: { preferredUsername: usernameWithDomain(group) },
:to="{ text: displayName(group),
name: RouteName.GROUP, },
params: { preferredUsername: usernameWithDomain(group) }, {
}" name: RouteName.POSTS,
>{{ group.name || group.preferredUsername }}</router-link params: { preferredUsername: usernameWithDomain(group) },
> text: $t('Posts'),
<b-skeleton v-else :animated="true"></b-skeleton> },
</li> ]"
<li class="is-active"> />
<router-link
v-if="group"
:to="{
name: RouteName.POSTS,
params: { preferredUsername: usernameWithDomain(group) },
}"
>{{ $t("Posts") }}</router-link
>
<b-skeleton v-else :animated="true"></b-skeleton>
</li>
</ul>
</nav>
<section> <section>
<div class="intro"> <div class="intro">
<p v-if="isCurrentActorMember"> <p v-if="isCurrentActorMember">
@ -84,7 +72,7 @@ import { IMember } from "@/types/actor/member.model";
import { FETCH_GROUP_POSTS } from "../../graphql/post"; import { FETCH_GROUP_POSTS } from "../../graphql/post";
import { Paginate } from "../../types/paginate"; import { Paginate } from "../../types/paginate";
import { IPost } from "../../types/post.model"; import { IPost } from "../../types/post.model";
import { usernameWithDomain } from "../../types/actor"; import { usernameWithDomain, displayName } from "../../types/actor";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
import MultiPostListItem from "../../components/Post/MultiPostListItem.vue"; import MultiPostListItem from "../../components/Post/MultiPostListItem.vue";
@ -148,6 +136,8 @@ export default class PostList extends mixins(GroupMixin) {
usernameWithDomain = usernameWithDomain; usernameWithDomain = usernameWithDomain;
displayName = displayName;
POSTS_PAGE_LIMIT = POSTS_PAGE_LIMIT; POSTS_PAGE_LIMIT = POSTS_PAGE_LIMIT;
get isCurrentActorMember(): boolean { get isCurrentActorMember(): boolean {

View File

@ -1,80 +1,38 @@
<template> <template>
<div class="container section" v-if="resource"> <div class="container section" v-if="resource">
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav :links="breadcrumbLinks">
<ul> <li>
<li> <b-dropdown aria-role="list">
<router-link <b-button class="button is-primary" slot="trigger">+</b-button>
:to="{
name: RouteName.GROUP,
params: { preferredUsername: usernameWithDomain(resource.actor) },
}"
>{{ resource.actor.name }}</router-link
>
</li>
<li>
<router-link
:to="{
name: RouteName.RESOURCE_FOLDER_ROOT,
params: { preferredUsername: usernameWithDomain(resource.actor) },
}"
>{{ $t("Resources") }}</router-link
>
</li>
<li
:class="{
'is-active':
index + 1 === ResourceMixin.resourcePathArray(resource).length,
}"
v-for="(pathFragment, index) in filteredPath"
:key="pathFragment"
>
<router-link
:to="{
name: RouteName.RESOURCE_FOLDER,
params: {
path: ResourceMixin.resourcePathArray(resource).slice(
0,
index + 1
),
preferredUsername: usernameWithDomain(resource.actor),
},
}"
>{{ pathFragment }}</router-link
>
</li>
<li>
<b-dropdown aria-role="list">
<b-button class="button is-primary" slot="trigger">+</b-button>
<b-dropdown-item aria-role="listitem" @click="createFolderModal"> <b-dropdown-item aria-role="listitem" @click="createFolderModal">
<b-icon icon="folder" /> <b-icon icon="folder" />
{{ $t("New folder") }} {{ $t("New folder") }}
</b-dropdown-item> </b-dropdown-item>
<b-dropdown-item <b-dropdown-item
aria-role="listitem" aria-role="listitem"
@click="createLinkResourceModal = true" @click="createLinkResourceModal = true"
> >
<b-icon icon="link" /> <b-icon icon="link" />
{{ $t("New link") }} {{ $t("New link") }}
</b-dropdown-item> </b-dropdown-item>
<hr <hr
role="presentation" role="presentation"
class="dropdown-divider" class="dropdown-divider"
v-if="resourceProviders.length" v-if="resourceProviders.length"
/> />
<b-dropdown-item <b-dropdown-item
aria-role="listitem" aria-role="listitem"
v-for="resourceProvider in resourceProviders" v-for="resourceProvider in resourceProviders"
:key="resourceProvider.software" :key="resourceProvider.software"
@click="createResourceFromProvider(resourceProvider)" @click="createResourceFromProvider(resourceProvider)"
> >
<b-icon :icon="mapServiceTypeToIcon[resourceProvider.software]" /> <b-icon :icon="mapServiceTypeToIcon[resourceProvider.software]" />
{{ createSentenceForType(resourceProvider.software) }} {{ createSentenceForType(resourceProvider.software) }}
</b-dropdown-item> </b-dropdown-item>
</b-dropdown> </b-dropdown>
</li> </li>
</ul> </breadcrumbs-nav>
</nav>
<section> <section>
<p v-if="resource.path === '/'" class="module-description"> <p v-if="resource.path === '/'" class="module-description">
{{ {{
@ -276,7 +234,7 @@ import ResourceItem from "@/components/Resource/ResourceItem.vue";
import FolderItem from "@/components/Resource/FolderItem.vue"; import FolderItem from "@/components/Resource/FolderItem.vue";
import Draggable from "vuedraggable"; import Draggable from "vuedraggable";
import { CURRENT_ACTOR_CLIENT } from "../../graphql/actor"; import { CURRENT_ACTOR_CLIENT } from "../../graphql/actor";
import { IActor, usernameWithDomain } from "../../types/actor"; import { displayName, IActor, usernameWithDomain } from "../../types/actor";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
import { import {
IResource, IResource,
@ -764,6 +722,40 @@ export default class Resources extends Mixins(ResourceMixin) {
} }
} }
} }
get breadcrumbLinks() {
if (!this.resource?.actor) return [];
const resourceActor = this.resource.actor;
const links = [
{
name: RouteName.GROUP,
params: { preferredUsername: usernameWithDomain(this.resource.actor) },
text: displayName(this.resource.actor),
},
{
name: RouteName.RESOURCE_FOLDER_ROOT,
params: { preferredUsername: usernameWithDomain(this.resource.actor) },
text: this.$t("Resources") as string,
},
];
links.push(
...this.filteredPath.map((pathFragment, index) => {
return {
name: RouteName.RESOURCE_FOLDER,
params: {
path: ResourceMixin.resourcePathArray(this.resource).slice(
0,
index + 1
) as unknown as string,
preferredUsername: usernameWithDomain(resourceActor),
},
text: pathFragment,
};
})
);
return links;
}
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -1,19 +1,17 @@
<template> <template>
<div v-if="loggedUser"> <div v-if="loggedUser">
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> {
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS }">{{ name: RouteName.ACCOUNT_SETTINGS,
$t("Account") text: $t('Account'),
}}</router-link> },
</li> {
<li class="is-active"> name: RouteName.ACCOUNT_SETTINGS_GENERAL,
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS_GENERAL }">{{ text: $t('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>

View File

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

View File

@ -1,19 +1,17 @@
<template> <template>
<div> <div>
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> {
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS }">{{ name: RouteName.ACCOUNT_SETTINGS,
$t("Account") text: $t('Account'),
}}</router-link> },
</li> {
<li class="is-active"> name: RouteName.PREFERENCES,
<router-link :to="{ name: RouteName.PREFERENCES }">{{ text: $t('Preferences'),
$t("Preferences") },
}}</router-link> ]"
</li> />
</ul>
</nav>
<div> <div>
<b-field :label="$t('Language')" label-for="setting-language"> <b-field :label="$t('Language')" label-for="setting-language">
<b-select <b-select

View File

@ -1,35 +1,29 @@
<template> <template>
<section class="section container" v-if="todo"> <section class="section container" v-if="todo">
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> {
<router-link name: RouteName.GROUP,
:to="{ params: {
name: RouteName.GROUP, preferredUsername: usernameWithDomain(todo.todoList.actor),
params: { },
preferredUsername: todo.todoList.actor.preferredUsername, text: displayName(todo.todoList.actor),
}, },
}" {
>{{ todo.todoList.actor.name }}</router-link name: RouteName.TODO_LISTS,
> params: {
</li> preferredUsername: usernameWithDomain(todo.todoList.actor),
<li> },
<router-link text: $t('Task lists'),
:to="{ },
name: RouteName.TODO_LIST, {
params: { id: todo.todoList.id }, name: RouteName.TODO_LIST,
}" params: { id: todo.todoList.id },
> text: todo.todoList.title,
{{ todo.todoList.title }} },
</router-link> { name: RouteName.TODO, text: todo.title },
</li> ]"
<li class="is-active"> />
<router-link :to="{ name: RouteName.TODO }" aria-current="page">
{{ todo.title }}
</router-link>
</li>
</ul>
</nav>
<full-todo :todo="todo" /> <full-todo :todo="todo" />
</section> </section>
</template> </template>
@ -39,6 +33,7 @@ import { GET_TODO } from "@/graphql/todos";
import { ITodo } from "@/types/todos"; import { ITodo } from "@/types/todos";
import FullTodo from "@/components/Todo/FullTodo.vue"; import FullTodo from "@/components/Todo/FullTodo.vue";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
import { displayName, usernameWithDomain } from "@/types/actor";
@Component({ @Component({
components: { components: {
@ -70,5 +65,9 @@ export default class Todo extends Vue {
todo!: ITodo; todo!: ITodo;
RouteName = RouteName; RouteName = RouteName;
usernameWithDomain = usernameWithDomain;
displayName = displayName;
} }
</script> </script>

View File

@ -1,34 +1,24 @@
<template> <template>
<section class="container section" v-if="todoList"> <section class="container section" v-if="todoList">
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> {
<router-link name: RouteName.GROUP,
:to="{ params: { preferredUsername: usernameWithDomain(todoList.actor) },
name: RouteName.GROUP, text: displayName(group),
params: { preferredUsername: todoList.actor.preferredUsername }, },
}" {
>{{ todoList.actor.name }}</router-link name: RouteName.TODO_LISTS,
> params: { preferredUsername: usernameWithDomain(todoList.actor) },
</li> text: $t('Task lists'),
<li> },
<router-link {
:to="{ name: RouteName.TODO_LIST,
name: RouteName.TODO_LISTS, params: { id: todoList.id },
params: { preferredUsername: todoList.actor.preferredUsername }, text: todoList.title,
}" },
>{{ $t("Task lists") }}</router-link ]"
> />
</li>
<li class="is-active">
<router-link
:to="{ name: RouteName.TODO_LIST, params: { id: todoList.id } }"
>
{{ todoList.title }}
</router-link>
</li>
</ul>
</nav>
<h2 class="title">{{ todoList.title }}</h2> <h2 class="title">{{ todoList.title }}</h2>
<div v-for="todo in todoList.todos.elements" :key="todo.id"> <div v-for="todo in todoList.todos.elements" :key="todo.id">
<compact-todo :todo="todo" /> <compact-todo :todo="todo" />
@ -48,7 +38,7 @@ import { ITodo } from "@/types/todos";
import { CREATE_TODO, FETCH_TODO_LIST } from "@/graphql/todos"; import { CREATE_TODO, FETCH_TODO_LIST } from "@/graphql/todos";
import CompactTodo from "@/components/Todo/CompactTodo.vue"; import CompactTodo from "@/components/Todo/CompactTodo.vue";
import { CURRENT_ACTOR_CLIENT } from "@/graphql/actor"; import { CURRENT_ACTOR_CLIENT } from "@/graphql/actor";
import { IActor } from "@/types/actor"; import { displayName, IActor, usernameWithDomain } from "@/types/actor";
import { ITodoList } from "@/types/todolist"; import { ITodoList } from "@/types/todolist";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
import { ApolloCache, FetchResult, InMemoryCache } from "@apollo/client/core"; import { ApolloCache, FetchResult, InMemoryCache } from "@apollo/client/core";
@ -89,6 +79,10 @@ export default class TodoList extends Vue {
RouteName = RouteName; RouteName = RouteName;
displayName = displayName;
usernameWithDomain = usernameWithDomain;
async createNewTodo(): Promise<void> { async createNewTodo(): Promise<void> {
await this.$apollo.mutate({ await this.$apollo.mutate({
mutation: CREATE_TODO, mutation: CREATE_TODO,

View File

@ -1,27 +1,19 @@
<template> <template>
<div class="container section" v-if="group"> <div class="container section" v-if="group">
<nav class="breadcrumb" aria-label="breadcrumbs"> <breadcrumbs-nav
<ul> :links="[
<li> {
<router-link name: RouteName.GROUP,
:to="{ params: { preferredUsername: usernameWithDomain(group) },
name: RouteName.GROUP, text: displayName(group),
params: { preferredUsername: usernameWithDomain(group) }, },
}" {
>{{ group.name }}</router-link name: RouteName.TODO_LISTS,
> params: { preferredUsername: usernameWithDomain(group) },
</li> text: $t('Task lists'),
<li class="is-active"> },
<router-link ]"
:to="{ />
name: RouteName.TODO_LISTS,
params: { preferredUsername: usernameWithDomain(group) },
}"
>{{ $t("Task lists") }}</router-link
>
</li>
</ul>
</nav>
<section> <section>
<p> <p>
{{ {{
@ -61,7 +53,7 @@
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"; import { Component, Prop, Vue } from "vue-property-decorator";
import { FETCH_GROUP } from "@/graphql/group"; import { FETCH_GROUP } from "@/graphql/group";
import { IGroup, usernameWithDomain } from "@/types/actor"; import { IGroup, usernameWithDomain, displayName } from "@/types/actor";
import { CREATE_TODO_LIST } from "@/graphql/todos"; import { CREATE_TODO_LIST } from "@/graphql/todos";
import CompactTodo from "@/components/Todo/CompactTodo.vue"; import CompactTodo from "@/components/Todo/CompactTodo.vue";
import { ITodoList } from "@/types/todolist"; import { ITodoList } from "@/types/todolist";
@ -108,6 +100,8 @@ export default class TodoLists extends Vue {
usernameWithDomain = usernameWithDomain; usernameWithDomain = usernameWithDomain;
displayName = displayName;
get todoLists(): ITodoList[] { get todoLists(): ITodoList[] {
return this.group.todoLists.elements; return this.group.todoLists.elements;
} }