From 6353c4f3728241e56bf6914e632902e8bec4c95c Mon Sep 17 00:00:00 2001
From: Thomas Citharel <tcit@tcit.fr>
Date: Tue, 25 May 2021 16:21:29 +0200
Subject: [PATCH] Fix missing metainfo on some views

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
---
 js/src/App.vue                                |  5 +++++
 js/src/views/About.vue                        |  9 ++++++++
 js/src/views/About/AboutInstance.vue          |  9 ++++++++
 js/src/views/About/Glossary.vue               |  5 +++++
 js/src/views/About/Privacy.vue                |  5 +++++
 js/src/views/About/Rules.vue                  |  5 +++++
 js/src/views/About/Terms.vue                  |  5 +++++
 js/src/views/Account/IdentityPicker.vue       |  5 +++++
 js/src/views/Account/Profile.vue              |  5 +++++
 js/src/views/Account/Register.vue             |  5 +++++
 .../views/Account/children/EditIdentity.vue   | 14 +++++++++++++
 js/src/views/Admin/AdminGroupProfile.vue      |  8 +++++++
 js/src/views/Admin/Dashboard.vue              |  1 -
 js/src/views/Admin/Follows.vue                |  5 +++++
 js/src/views/Admin/GroupProfiles.vue          |  5 +++++
 js/src/views/Admin/Profiles.vue               |  5 +++++
 js/src/views/Admin/Settings.vue               |  5 +++++
 js/src/views/Admin/Users.vue                  |  5 +++++
 js/src/views/Discussions/Create.vue           |  4 ----
 js/src/views/Discussions/Discussion.vue       |  2 --
 js/src/views/Discussions/DiscussionsList.vue  |  4 ----
 js/src/views/Error.vue                        |  8 ++++++-
 js/src/views/Event/Edit.vue                   |  1 -
 js/src/views/Event/Event.vue                  |  1 -
 js/src/views/Event/EventList.vue              |  5 +++++
 js/src/views/Event/GroupEvents.vue            | 10 +++++++++
 js/src/views/Event/MyEvents.vue               |  1 -
 js/src/views/Event/Participants.vue           |  5 +++++
 js/src/views/Group/Create.vue                 |  5 +++++
 js/src/views/Group/Group.vue                  |  1 -
 js/src/views/Group/GroupFollowers.vue         |  5 +++++
 js/src/views/Group/GroupMembers.vue           |  5 +++++
 js/src/views/Group/GroupSettings.vue          |  5 +++++
 js/src/views/Interact.vue                     |  5 +++++
 js/src/views/PageNotFound.vue                 |  5 +++++
 js/src/views/Resources/ResourceFolder.vue     | 21 +++++++++++++++++++
 js/src/views/Settings.vue                     |  5 +++++
 js/src/views/Settings/AccountSettings.vue     |  5 +++++
 js/src/views/Settings/Notifications.vue       |  5 +++++
 js/src/views/Settings/Preferences.vue         |  5 +++++
 40 files changed, 203 insertions(+), 16 deletions(-)

diff --git a/js/src/App.vue b/js/src/App.vue
index f3e75139e..1030cff25 100644
--- a/js/src/App.vue
+++ b/js/src/App.vue
@@ -63,6 +63,11 @@ import { ICurrentUser } from "./types/current-user.model";
       import(/* webpackChunkName: "editor" */ "./components/Error.vue"),
     "mobilizon-footer": Footer,
   },
+  metaInfo() {
+    return {
+      titleTemplate: "%s | Mobilizon",
+    };
+  },
 })
 export default class App extends Vue {
   config!: IConfig;
diff --git a/js/src/views/About.vue b/js/src/views/About.vue
index e15166b4d..1f9f42416 100644
--- a/js/src/views/About.vue
+++ b/js/src/views/About.vue
@@ -108,6 +108,15 @@ import RouteName from "../router/name";
       query: CONFIG,
     },
   },
+  metaInfo() {
+    return {
+      title: this.$t("About {instance}", {
+        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+        // @ts-ignore
+        instance: this?.config?.name,
+      }) as string,
+    };
+  },
 })
 export default class About extends Vue {
   config!: IConfig;
diff --git a/js/src/views/About/AboutInstance.vue b/js/src/views/About/AboutInstance.vue
index 8dcb96433..7bf25f7e4 100644
--- a/js/src/views/About/AboutInstance.vue
+++ b/js/src/views/About/AboutInstance.vue
@@ -135,6 +135,15 @@ import langs from "../../i18n/langs.json";
   components: {
     InstanceContactLink,
   },
+  metaInfo() {
+    return {
+      title: this.$t("About {instance}", {
+        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+        // @ts-ignore
+        instance: this?.config?.name,
+      }) as string,
+    };
+  },
 })
 export default class AboutInstance extends Vue {
   config!: IConfig;
diff --git a/js/src/views/About/Glossary.vue b/js/src/views/About/Glossary.vue
index 33b74cb15..6ef9e7a1c 100644
--- a/js/src/views/About/Glossary.vue
+++ b/js/src/views/About/Glossary.vue
@@ -73,6 +73,11 @@ import { IConfig } from "../../types/config.model";
   apollo: {
     config: ABOUT,
   },
+  metaInfo() {
+    return {
+      title: this.$t("Glossary") as string,
+    };
+  },
 })
 export default class Glossary extends Vue {
   config!: IConfig;
diff --git a/js/src/views/About/Privacy.vue b/js/src/views/About/Privacy.vue
index 4de1084cb..66e9d5d3d 100644
--- a/js/src/views/About/Privacy.vue
+++ b/js/src/views/About/Privacy.vue
@@ -29,6 +29,11 @@ import { InstancePrivacyType } from "@/types/enums";
       },
     },
   },
+  metaInfo() {
+    return {
+      title: this.$t("Privacy Policy") as string,
+    };
+  },
 })
 export default class Privacy extends Vue {
   config!: IConfig;
diff --git a/js/src/views/About/Rules.vue b/js/src/views/About/Rules.vue
index 2dd0059f3..bf58f5f62 100644
--- a/js/src/views/About/Rules.vue
+++ b/js/src/views/About/Rules.vue
@@ -18,6 +18,11 @@ import RouteName from "../../router/name";
       query: RULES,
     },
   },
+  metaInfo() {
+    return {
+      title: this.$t("Rules") as string,
+    };
+  },
 })
 export default class Rules extends Vue {
   config!: IConfig;
diff --git a/js/src/views/About/Terms.vue b/js/src/views/About/Terms.vue
index 5a165a785..dc20db2ad 100644
--- a/js/src/views/About/Terms.vue
+++ b/js/src/views/About/Terms.vue
@@ -25,6 +25,11 @@ import { InstanceTermsType } from "@/types/enums";
       },
     },
   },
+  metaInfo() {
+    return {
+      title: this.$t("Terms") as string,
+    };
+  },
 })
 export default class Terms extends Vue {
   config!: IConfig;
diff --git a/js/src/views/Account/IdentityPicker.vue b/js/src/views/Account/IdentityPicker.vue
index f98de9581..071ea193d 100644
--- a/js/src/views/Account/IdentityPicker.vue
+++ b/js/src/views/Account/IdentityPicker.vue
@@ -47,6 +47,11 @@ import { IDENTITIES } from "@/graphql/actor";
       query: IDENTITIES,
     },
   },
+  metaInfo() {
+    return {
+      title: this.$t("Identities") as string,
+    };
+  },
 })
 export default class IdentityPicker extends Vue {
   @Prop() value!: IActor;
diff --git a/js/src/views/Account/Profile.vue b/js/src/views/Account/Profile.vue
index c2dbdbc1b..b7d98ed2f 100644
--- a/js/src/views/Account/Profile.vue
+++ b/js/src/views/Account/Profile.vue
@@ -124,6 +124,11 @@ import { CREATE_FEED_TOKEN_ACTOR } from "../../graphql/feed_tokens";
   components: {
     EventCard,
   },
+  metaInfo() {
+    return {
+      title: this.$t("Profile") as string,
+    };
+  },
 })
 export default class Profile extends Vue {
   @Prop({ type: String, required: true }) name!: string;
diff --git a/js/src/views/Account/Register.vue b/js/src/views/Account/Register.vue
index 0bb1ce8e4..c6477c653 100644
--- a/js/src/views/Account/Register.vue
+++ b/js/src/views/Account/Register.vue
@@ -134,6 +134,11 @@ import { ApolloCache, FetchResult, InMemoryCache } from "@apollo/client/core";
   apollo: {
     config: CONFIG,
   },
+  metaInfo() {
+    return {
+      title: this.$t("Register") as string,
+    };
+  },
 })
 export default class Register extends mixins(identityEditionMixin) {
   @Prop({ type: String, required: true }) email!: string;
diff --git a/js/src/views/Account/children/EditIdentity.vue b/js/src/views/Account/children/EditIdentity.vue
index 367b0226e..6da675920 100644
--- a/js/src/views/Account/children/EditIdentity.vue
+++ b/js/src/views/Account/children/EditIdentity.vue
@@ -263,6 +263,20 @@ import { ApolloCache, FetchResult, InMemoryCache } from "@apollo/client/core";
     },
     config: CONFIG,
   },
+  metaInfo() {
+    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+    // @ts-ignore
+    const { isUpdate, identityName } = this;
+    let title = this.$t("Create a new profile") as string;
+    if (isUpdate) {
+      title = this.$t("Edit profile {profile}", {
+        profile: identityName,
+      }) as string;
+    }
+    return {
+      title,
+    };
+  },
 })
 export default class EditIdentity extends mixins(identityEditionMixin) {
   @Prop({ type: Boolean }) isUpdate!: boolean;
diff --git a/js/src/views/Admin/AdminGroupProfile.vue b/js/src/views/Admin/AdminGroupProfile.vue
index 5237382c8..2cbec4711 100644
--- a/js/src/views/Admin/AdminGroupProfile.vue
+++ b/js/src/views/Admin/AdminGroupProfile.vue
@@ -321,6 +321,14 @@ const MEMBERS_PER_PAGE = 3;
     ActorCard,
     EmptyContent,
   },
+  metaInfo() {
+    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+    // @ts-ignore
+    const { group } = this;
+    return {
+      title: group ? group.name || usernameWithDomain(group) : "",
+    };
+  },
 })
 export default class AdminGroupProfile extends Vue {
   @Prop({ required: true }) id!: string;
diff --git a/js/src/views/Admin/Dashboard.vue b/js/src/views/Admin/Dashboard.vue
index 9c2d4d613..0e7ec6b88 100644
--- a/js/src/views/Admin/Dashboard.vue
+++ b/js/src/views/Admin/Dashboard.vue
@@ -158,7 +158,6 @@ import RouteName from "../../router/name";
   metaInfo() {
     return {
       title: this.$t("Administration") as string,
-      titleTemplate: "%s | Mobilizon",
     };
   },
 })
diff --git a/js/src/views/Admin/Follows.vue b/js/src/views/Admin/Follows.vue
index 2d9b6e599..d23a53cbc 100644
--- a/js/src/views/Admin/Follows.vue
+++ b/js/src/views/Admin/Follows.vue
@@ -87,6 +87,11 @@ import RouteName from "../../router/name";
       },
     },
   },
+  metaInfo() {
+    return {
+      title: this.$t("Follows") as string,
+    };
+  },
 })
 export default class Follows extends Vue {
   RouteName = RouteName;
diff --git a/js/src/views/Admin/GroupProfiles.vue b/js/src/views/Admin/GroupProfiles.vue
index 261bfcb07..3005cd486 100644
--- a/js/src/views/Admin/GroupProfiles.vue
+++ b/js/src/views/Admin/GroupProfiles.vue
@@ -122,6 +122,11 @@ const PROFILES_PER_PAGE = 10;
       },
     },
   },
+  metaInfo() {
+    return {
+      title: this.$t("Groups") as string,
+    };
+  },
   components: {
     EmptyContent,
   },
diff --git a/js/src/views/Admin/Profiles.vue b/js/src/views/Admin/Profiles.vue
index 682f8b5eb..edde5d05b 100644
--- a/js/src/views/Admin/Profiles.vue
+++ b/js/src/views/Admin/Profiles.vue
@@ -126,6 +126,11 @@ const PROFILES_PER_PAGE = 10;
   components: {
     EmptyContent,
   },
+  metaInfo() {
+    return {
+      title: this.$t("Profiles") as string,
+    };
+  },
 })
 export default class Profiles extends Vue {
   PROFILES_PER_PAGE = PROFILES_PER_PAGE;
diff --git a/js/src/views/Admin/Settings.vue b/js/src/views/Admin/Settings.vue
index 10214a49c..714dbb5d9 100644
--- a/js/src/views/Admin/Settings.vue
+++ b/js/src/views/Admin/Settings.vue
@@ -347,6 +347,11 @@ import RouteName from "../../router/name";
     adminSettings: ADMIN_SETTINGS,
     languages: LANGUAGES,
   },
+  metaInfo() {
+    return {
+      title: this.$t("Settings") as string,
+    };
+  },
 })
 export default class Settings extends Vue {
   adminSettings!: IAdminSettings;
diff --git a/js/src/views/Admin/Users.vue b/js/src/views/Admin/Users.vue
index 942304174..8f4de705b 100644
--- a/js/src/views/Admin/Users.vue
+++ b/js/src/views/Admin/Users.vue
@@ -132,6 +132,11 @@ const USERS_PER_PAGE = 10;
       },
     },
   },
+  metaInfo() {
+    return {
+      title: this.$t("Users") as string,
+    };
+  },
 })
 export default class Users extends Vue {
   USERS_PER_PAGE = USERS_PER_PAGE;
diff --git a/js/src/views/Discussions/Create.vue b/js/src/views/Discussions/Create.vue
index f7ff343e2..a5ae11bf0 100644
--- a/js/src/views/Discussions/Create.vue
+++ b/js/src/views/Discussions/Create.vue
@@ -51,11 +51,7 @@ import RouteName from "../../router/name";
   },
   metaInfo() {
     return {
-      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
-      // @ts-ignore
       title: this.$t("Create a discussion") as string,
-      // all titles will be injected into this template
-      titleTemplate: "%s | Mobilizon",
     };
   },
 })
diff --git a/js/src/views/Discussions/Discussion.vue b/js/src/views/Discussions/Discussion.vue
index 522c18642..eeff5c07d 100644
--- a/js/src/views/Discussions/Discussion.vue
+++ b/js/src/views/Discussions/Discussion.vue
@@ -216,8 +216,6 @@ import { ApolloCache, FetchResult, InMemoryCache } from "@apollo/client/core";
       // eslint-disable-next-line @typescript-eslint/ban-ts-comment
       // @ts-ignore
       title: this.discussion.title,
-      // all titles will be injected into this template
-      titleTemplate: "%s | Mobilizon",
     };
   },
 })
diff --git a/js/src/views/Discussions/DiscussionsList.vue b/js/src/views/Discussions/DiscussionsList.vue
index 1d552c6bf..a49c901c8 100644
--- a/js/src/views/Discussions/DiscussionsList.vue
+++ b/js/src/views/Discussions/DiscussionsList.vue
@@ -150,11 +150,7 @@ const DISCUSSIONS_PER_PAGE = 10;
   },
   metaInfo() {
     return {
-      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
-      // @ts-ignore
       title: this.$t("Discussions") as string,
-      // all titles will be injected into this template
-      titleTemplate: "%s | Mobilizon",
     };
   },
 })
diff --git a/js/src/views/Error.vue b/js/src/views/Error.vue
index e68e246e7..fb69a23ab 100644
--- a/js/src/views/Error.vue
+++ b/js/src/views/Error.vue
@@ -14,7 +14,13 @@
 import { ErrorCode } from "@/types/enums";
 import { Component, Vue } from "vue-property-decorator";
 
-@Component
+@Component({
+  metaInfo() {
+    return {
+      title: this.$t("Error") as string,
+    };
+  },
+})
 export default class ErrorPage extends Vue {
   code: ErrorCode | null = null;
 
diff --git a/js/src/views/Event/Edit.vue b/js/src/views/Event/Edit.vue
index 3a748cc4d..d30d3f76f 100644
--- a/js/src/views/Event/Edit.vue
+++ b/js/src/views/Event/Edit.vue
@@ -526,7 +526,6 @@ const DEFAULT_LIMIT_NUMBER_OF_PLACES = 10;
       title: (this.isUpdate
         ? this.$t("Event edition")
         : this.$t("Event creation")) as string,
-      titleTemplate: "%s | Mobilizon",
     };
   },
 })
diff --git a/js/src/views/Event/Event.vue b/js/src/views/Event/Event.vue
index d6ee18f80..696841663 100755
--- a/js/src/views/Event/Event.vue
+++ b/js/src/views/Event/Event.vue
@@ -744,7 +744,6 @@ import { ApolloCache, FetchResult, InMemoryCache } from "@apollo/client/core";
       // eslint-disable-next-line @typescript-eslint/ban-ts-comment
       // @ts-ignore
       title: this.eventTitle,
-      titleTemplate: "%s | Mobilizon",
       meta: [
         // eslint-disable-next-line @typescript-eslint/ban-ts-comment
         // @ts-ignore
diff --git a/js/src/views/Event/EventList.vue b/js/src/views/Event/EventList.vue
index 30f3f6801..89da54b7f 100644
--- a/js/src/views/Event/EventList.vue
+++ b/js/src/views/Event/EventList.vue
@@ -28,6 +28,11 @@ import { IEvent } from "../../types/event.model";
   components: {
     EventCard,
   },
+  metaInfo() {
+    return {
+      title: this.$t("Event list") as string,
+    };
+  },
 })
 export default class EventList extends Vue {
   @Prop(String) location!: string;
diff --git a/js/src/views/Event/GroupEvents.vue b/js/src/views/Event/GroupEvents.vue
index ed1f20892..7a5546eae 100644
--- a/js/src/views/Event/GroupEvents.vue
+++ b/js/src/views/Event/GroupEvents.vue
@@ -129,6 +129,16 @@ const EVENTS_PAGE_LIMIT = 10;
     Subtitle,
     EventListViewCard,
   },
+  metaInfo() {
+    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+    // @ts-ignore
+    const { group } = this;
+    return {
+      title: this.$t("{group} events", {
+        group: group.name || usernameWithDomain(group),
+      }) as string,
+    };
+  },
 })
 export default class GroupEvents extends mixins(GroupMixin) {
   group!: IGroup;
diff --git a/js/src/views/Event/MyEvents.vue b/js/src/views/Event/MyEvents.vue
index 70faea02a..788518dd8 100644
--- a/js/src/views/Event/MyEvents.vue
+++ b/js/src/views/Event/MyEvents.vue
@@ -187,7 +187,6 @@ import Subtitle from "../../components/Utils/Subtitle.vue";
   metaInfo() {
     return {
       title: this.$t("My events") as string,
-      titleTemplate: "%s | Mobilizon",
     };
   },
 })
diff --git a/js/src/views/Event/Participants.vue b/js/src/views/Event/Participants.vue
index 47e1ac9a7..921614873 100644
--- a/js/src/views/Event/Participants.vue
+++ b/js/src/views/Event/Participants.vue
@@ -261,6 +261,11 @@ const MESSAGE_ELLIPSIS_LENGTH = 130;
     ellipsize: (text?: string) =>
       text && text.substr(0, MESSAGE_ELLIPSIS_LENGTH).concat("…"),
   },
+  metaInfo() {
+    return {
+      title: this.$t("Participants") as string,
+    };
+  },
 })
 export default class Participants extends Vue {
   @Prop({ required: true }) eventId!: string;
diff --git a/js/src/views/Group/Create.vue b/js/src/views/Group/Create.vue
index 024c0e7dd..871b19de0 100644
--- a/js/src/views/Group/Create.vue
+++ b/js/src/views/Group/Create.vue
@@ -108,6 +108,11 @@ import { ApolloCache, FetchResult, InMemoryCache } from "@apollo/client/core";
     },
     config: CONFIG,
   },
+  metaInfo() {
+    return {
+      title: this.$t("Create a new group") as string,
+    };
+  },
 })
 export default class CreateGroup extends mixins(IdentityEditionMixin) {
   currentActor!: IPerson;
diff --git a/js/src/views/Group/Group.vue b/js/src/views/Group/Group.vue
index d8c2a4385..30d5de094 100644
--- a/js/src/views/Group/Group.vue
+++ b/js/src/views/Group/Group.vue
@@ -571,7 +571,6 @@ import { PERSON_MEMBERSHIP_GROUP } from "@/graphql/actor";
       // eslint-disable-next-line @typescript-eslint/ban-ts-comment
       // @ts-ignore
       title: this.groupTitle,
-      titleTemplate: "%s | Mobilizon",
       meta: [
         // eslint-disable-next-line @typescript-eslint/ban-ts-comment
         // @ts-ignore
diff --git a/js/src/views/Group/GroupFollowers.vue b/js/src/views/Group/GroupFollowers.vue
index 81b5de2ba..13a44f469 100644
--- a/js/src/views/Group/GroupFollowers.vue
+++ b/js/src/views/Group/GroupFollowers.vue
@@ -160,6 +160,11 @@ import { Paginate } from "@/types/paginate";
   components: {
     EmptyContent,
   },
+  metaInfo() {
+    return {
+      title: this.$t("Group Followers") as string,
+    };
+  },
 })
 export default class GroupFollowers extends mixins(GroupMixin) {
   loading = true;
diff --git a/js/src/views/Group/GroupMembers.vue b/js/src/views/Group/GroupMembers.vue
index 3e6743dcb..e9e85a613 100644
--- a/js/src/views/Group/GroupMembers.vue
+++ b/js/src/views/Group/GroupMembers.vue
@@ -265,6 +265,11 @@ import EmptyContent from "@/components/Utils/EmptyContent.vue";
   components: {
     EmptyContent,
   },
+  metaInfo() {
+    return {
+      title: this.$t("Group Members") as string,
+    };
+  },
 })
 export default class GroupMembers extends mixins(GroupMixin) {
   loading = true;
diff --git a/js/src/views/Group/GroupSettings.vue b/js/src/views/Group/GroupSettings.vue
index a85d5a72a..b1ed0036a 100644
--- a/js/src/views/Group/GroupSettings.vue
+++ b/js/src/views/Group/GroupSettings.vue
@@ -196,6 +196,11 @@ import { ErrorResponse } from "@apollo/client/link/error";
   apollo: {
     config: CONFIG,
   },
+  metaInfo() {
+    return {
+      title: this.$t("Group settings") as string,
+    };
+  },
 })
 export default class GroupSettings extends mixins(GroupMixin) {
   loading = true;
diff --git a/js/src/views/Interact.vue b/js/src/views/Interact.vue
index d3a5d77dc..a320bd5bf 100644
--- a/js/src/views/Interact.vue
+++ b/js/src/views/Interact.vue
@@ -83,6 +83,11 @@ import { GraphQLError } from "graphql";
       },
     },
   },
+  metaInfo() {
+    return {
+      title: this.$t("Interact with a remote content") as string,
+    };
+  },
 })
 export default class Interact extends Vue {
   interact!: IEvent | IGroup;
diff --git a/js/src/views/PageNotFound.vue b/js/src/views/PageNotFound.vue
index afce22e21..dc0412ad7 100644
--- a/js/src/views/PageNotFound.vue
+++ b/js/src/views/PageNotFound.vue
@@ -67,6 +67,11 @@ import RouteName from "../router/name";
   components: {
     BField,
   },
+  metaInfo() {
+    return {
+      title: this.$t("Page not found") as string,
+    };
+  },
 })
 export default class PageNotFound extends Vue {
   searchText = "";
diff --git a/js/src/views/Resources/ResourceFolder.vue b/js/src/views/Resources/ResourceFolder.vue
index 7e3435a4f..a321666ca 100644
--- a/js/src/views/Resources/ResourceFolder.vue
+++ b/js/src/views/Resources/ResourceFolder.vue
@@ -274,6 +274,19 @@ import { ApolloCache, FetchResult, InMemoryCache } from "@apollo/client/core";
     config: CONFIG,
     currentActor: CURRENT_ACTOR_CLIENT,
   },
+  metaInfo() {
+    return {
+      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+      // @ts-ignore
+      title: this.isRoot
+        ? (this.$t("Resources") as string)
+        : (this.$t("{folder} - Resources", {
+            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+            // @ts-ignore
+            folder: this.lastFragment,
+          }) as string),
+    };
+  },
 })
 export default class Resources extends Mixins(ResourceMixin) {
   @Prop({ required: true }) path!: string;
@@ -345,6 +358,14 @@ export default class Resources extends Mixins(ResourceMixin) {
     return [];
   }
 
+  get isRoot(): boolean {
+    return this.actualPath === "/";
+  }
+
+  get lastFragment(): string | undefined {
+    return this.filteredPath.slice(-1)[0];
+  }
+
   async createResource(): Promise<void> {
     if (!this.resource.actor) return;
     this.modalError = "";
diff --git a/js/src/views/Settings.vue b/js/src/views/Settings.vue
index 0874637e6..28f3a748b 100644
--- a/js/src/views/Settings.vue
+++ b/js/src/views/Settings.vue
@@ -28,6 +28,11 @@ import { ICurrentUser } from "../types/current-user.model";
     },
     currentUser: CURRENT_USER_CLIENT,
   },
+  metaInfo() {
+    return {
+      title: this.$t("Settings") as string,
+    };
+  },
 })
 export default class Settings extends Vue {
   RouteName = RouteName;
diff --git a/js/src/views/Settings/AccountSettings.vue b/js/src/views/Settings/AccountSettings.vue
index a4c3ee31c..8c1f222ca 100644
--- a/js/src/views/Settings/AccountSettings.vue
+++ b/js/src/views/Settings/AccountSettings.vue
@@ -233,6 +233,11 @@ import { logout, SELECTED_PROVIDERS } from "../../utils/auth";
   apollo: {
     loggedUser: LOGGED_USER,
   },
+  metaInfo() {
+    return {
+      title: this.$t("General settings") as string,
+    };
+  },
 })
 export default class AccountSettings extends Vue {
   @Ref("passwordForm") readonly passwordForm!: HTMLElement;
diff --git a/js/src/views/Settings/Notifications.vue b/js/src/views/Settings/Notifications.vue
index c27117e47..81e7a6ef2 100644
--- a/js/src/views/Settings/Notifications.vue
+++ b/js/src/views/Settings/Notifications.vue
@@ -235,6 +235,11 @@ import {
         ),
     },
   },
+  metaInfo() {
+    return {
+      title: this.$t("Notifications") as string,
+    };
+  },
 })
 export default class Notifications extends Vue {
   loggedUser!: IUser;
diff --git a/js/src/views/Settings/Preferences.vue b/js/src/views/Settings/Preferences.vue
index b779aec08..563d50586 100644
--- a/js/src/views/Settings/Preferences.vue
+++ b/js/src/views/Settings/Preferences.vue
@@ -123,6 +123,11 @@ import { Address, IAddress } from "@/types/address.model";
   components: {
     AddressAutoComplete,
   },
+  metaInfo() {
+    return {
+      title: this.$t("Preferences") as string,
+    };
+  },
 })
 export default class Preferences extends Vue {
   config!: IConfig;