Skip to content

Commit b16d196

Browse files
committed
Set page title per-route rather than per-view
Multiple views may be included in a page, so the previous approach was flawed
1 parent e3ac8a1 commit b16d196

27 files changed

+112
-251
lines changed

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
"@lumino/default-theme": "2.1.5",
3030
"@lumino/widgets": "2.3.2",
3131
"@mdi/js": "7.4.47",
32-
"@unhead/vue": "1.9.9",
3332
"@vueuse/core": "10.9.0",
3433
"apexcharts": "3.41.0",
3534
"axios": "1.6.8",

renovate.json

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,6 @@
5555
"matchPackageNames": ["sass"],
5656
"schedule": ["on the 25th day of the month"]
5757
},
58-
{
59-
"matchPackageNames": ["@unhead/vue"],
60-
"automerge": true,
61-
"schedule": ["on the 3rd day of the month"]
62-
},
6358
{
6459
"matchPackageNames": ["@vueuse/core"],
6560
"automerge": true,

src/components/cylc/commandMenu/Menu.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ export default {
253253
// Navigate to the corresponding workflow then open the log view
254254
// (no nav occurs if already on the correct workflow page)
255255
this.$router.push({
256-
name: 'workspace',
256+
name: 'Workspace',
257257
params: {
258258
workflowName: this.node.tokens.workflow
259259
}

src/components/cylc/workflow/Toolbar.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
131131
<v-spacer class="mx-0" />
132132

133133
<v-btn
134-
v-if="$route.name === 'workspace'"
134+
v-if="$route.name === 'Workspace'"
135135
class="add-view"
136136
color="primary"
137137
data-cy="add-view-btn"

src/layouts/Default.vue

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import { computed } from 'vue'
4242
import { useRoute } from 'vue-router'
4343
import { mapState } from 'vuex'
4444
import { store } from '@/store/index'
45+
import { allViews } from '@/views/views'
4546
import { Alert as AlertModel } from '@/model/Alert.model'
4647
import Alert from '@/components/core/Alert.vue'
4748
import Drawer from '@/components/cylc/Drawer.vue'
@@ -57,17 +58,15 @@ export default {
5758
const route = useRoute()
5859
/**
5960
* Views that display workflows. For these views, we do not
60-
* want to display the default Toolbar—the Workflow view
61+
* want to display the default Toolbar—the Workspace view
6162
* has its own Toolbar that communicates with the Workflow
6263
* component (e.g. the Workflow Toolbar owns a button that
6364
* triggers the action to add a new Tree or Table View, so the events
6465
* are passed down from the parent Workflow View).
6566
*/
6667
const workflowViews = [
67-
'workspace',
68-
'tree',
69-
'table',
70-
'graph',
68+
...allViews.keys(),
69+
'Workspace',
7170
]
7271
const { showNavBtn } = useNavBtn()
7372

src/main.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ import router from '@/router/index'
3131
import { store } from '@/store/index'
3232
import { createVuetify } from 'vuetify'
3333
import mitt from 'mitt'
34-
import { createHead, VueHeadMixin } from '@unhead/vue'
3534

35+
import 'nprogress/css/nprogress.css'
3636
import '@/styles/index.scss'
3737

3838
if (location.search) {
@@ -51,18 +51,14 @@ if (location.search) {
5151
// Normal app start
5252
const app = createApp(App)
5353

54-
const emitter = mitt()
55-
56-
app.mixin(VueHeadMixin)
57-
5854
app.use(store)
5955
app.use(router)
6056
app.use(createVuetify(vuetifyOptions))
6157
app.use(i18n)
62-
app.use(createHead())
6358
app.use(ServicesPlugin)
6459
app.use(CommandMenuPlugin)
6560

61+
const emitter = mitt()
6662
// Composition API:
6763
app.provide('eventBus', emitter)
6864
// Options API (legacy):

src/router/index.js

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@
2525

2626
import { createRouter, createWebHashHistory } from 'vue-router'
2727
import NProgress from 'nprogress'
28-
29-
import 'nprogress/css/nprogress.css'
28+
import { i18n } from '@/i18n'
3029

3130
import paths from '@/router/paths'
3231
import { store } from '@/store/index'
3332
import { Alert } from '@/model/Alert.model'
3433

34+
const defaultPageTitle = i18n.global.t('App.name')
35+
3536
NProgress.configure({ showSpinner: false })
3637

3738
function getRoute (path) {
@@ -42,6 +43,17 @@ function getRoute (path) {
4243
}
4344
}
4445

46+
/**
47+
* Return the page title for a particular route.
48+
* @param {import('vue-router').RouteLocation} route
49+
*/
50+
export function getPageTitle ({ meta, params }) {
51+
const extra = meta.getTitle?.(params) || meta.title
52+
return extra
53+
? `${defaultPageTitle} | ${extra}`
54+
: defaultPageTitle
55+
}
56+
4557
// Create a new router
4658
const router = createRouter({
4759
history: createWebHashHistory(),
@@ -65,27 +77,29 @@ router.beforeEach(async (to, from) => {
6577
store.commit('user/SET_USER', user)
6678
}
6779
if (!store.state.user.user.permissions?.includes('read')) {
68-
if (to.name !== 'noAuth') { // Avoid infinite redirect?
69-
return { name: 'noAuth' }
80+
if (to.name !== 'NoAuth') { // Avoid infinite redirect?
81+
return { name: 'NoAuth' }
7082
}
71-
} else if (to.name === 'noAuth') {
83+
} else if (to.name === 'NoAuth') {
7284
// If authorized, redirect no-auth page to home page
7385
return { path: '/' }
7486
}
7587

76-
if (to.name) {
77-
let title = to.name
78-
let workflowName = null
79-
if (to.meta.toolbar) {
80-
// When a workflow is being displayed, we set the title to a
81-
// different value.
82-
title = to.params.workflowName
83-
workflowName = to.params.workflowName
84-
}
85-
store.commit('app/setTitle', title)
86-
store.commit('workflows/SET_WORKFLOW_NAME', workflowName)
87-
store.dispatch('setAlert', null)
88+
// Set page title:
89+
document.title = getPageTitle(to)
90+
91+
// Set toolbar title:
92+
let title = to.name
93+
let workflowName = null
94+
if (to.meta.toolbar) {
95+
// When a workflow is being displayed, we set the title to a
96+
// different value.
97+
title = to.params.workflowName
98+
workflowName = to.params.workflowName
8899
}
100+
store.commit('app/setTitle', title)
101+
store.commit('workflows/SET_WORKFLOW_NAME', workflowName)
102+
store.dispatch('setAlert', null)
89103
})
90104

91105
router.afterEach(() => {

src/router/paths.js

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/**
1+
/*
22
* Copyright (C) NIWA & British Crown (Met Office) & Contributors.
33
*
44
* This program is free software: you can redistribute it and/or modify
@@ -17,75 +17,82 @@
1717

1818
import { i18n } from '@/i18n'
1919

20+
const workflowTitle = ({ workflowName: name }) => i18n.global.t('App.workflow', { name })
21+
2022
/**
2123
* Define all of your application routes here
2224
* for more information on routes, see the
2325
* official documentation https://router.vuejs.org/en/
26+
*
27+
* @type {import('vue-router').RouteRecordRaw[]} - except the `name` and
28+
* `component` fields which are automatically added in @/src/router/index.js
2429
*/
2530
export default [
2631
{
2732
path: '/',
2833
view: 'Dashboard',
29-
name: i18n.global.t('App.dashboard'),
3034
meta: {
35+
title: i18n.global.t('App.dashboard'),
3136
layout: 'default'
3237
}
3338
},
3439
{
3540
path: '/workflow-table',
36-
name: 'Workflow Table',
3741
view: 'WorkflowsTable',
3842
meta: {
43+
title: 'Workflow Table',
3944
layout: 'default'
4045
}
4146
},
4247
{
4348
path: '/workspace/:workflowName(.*)',
4449
view: 'Workspace',
45-
name: 'workspace',
4650
meta: {
51+
getTitle: workflowTitle,
4752
layout: 'default',
4853
toolbar: true
4954
},
5055
props: true
5156
},
5257
{
5358
path: '/user-profile',
54-
name: i18n.global.t('App.userProfile'),
5559
view: 'UserProfile',
5660
meta: {
61+
title: i18n.global.t('App.userProfile'),
5762
layout: 'default'
5863
}
5964
},
6065
{
6166
path: '/guide',
62-
name: 'Guide',
6367
view: 'Guide',
6468
meta: {
69+
title: i18n.global.t('App.guide'),
6570
layout: 'default'
6671
}
6772
},
6873
{
6974
path: '/graphiql',
7075
view: 'GraphiQL',
7176
meta: {
77+
title: 'GraphiQL',
7278
layout: 'empty'
7379
}
7480
},
7581
{
7682
path: '/:catchAll(.*)',
7783
view: 'NotFound',
7884
meta: {
85+
title: i18n.global.t('App.notFound'),
7986
layout: 'empty'
8087
}
8188
},
8289

8390
// the standalone views
8491
{
8592
path: '/workflows',
86-
name: i18n.global.t('App.workflows'),
8793
view: 'Workflows',
8894
meta: {
95+
title: i18n.global.t('App.workflows'),
8996
layout: 'default',
9097
toolbar: false,
9198
showSidebar: false
@@ -94,8 +101,8 @@ export default [
94101
{
95102
path: '/tree/:workflowName(.*)',
96103
view: 'Tree',
97-
name: 'tree',
98104
meta: {
105+
getTitle: workflowTitle,
99106
layout: 'default',
100107
toolbar: true,
101108
showSidebar: false
@@ -105,8 +112,8 @@ export default [
105112
{
106113
path: '/table/:workflowName(.*)',
107114
view: 'Table',
108-
name: 'table',
109115
meta: {
116+
getTitle: workflowTitle,
110117
layout: 'default',
111118
toolbar: true,
112119
showSidebar: false
@@ -116,8 +123,8 @@ export default [
116123
{
117124
path: '/graph/:workflowName(.*)',
118125
view: 'Graph',
119-
name: 'graph',
120126
meta: {
127+
getTitle: workflowTitle,
121128
layout: 'default',
122129
toolbar: true,
123130
showSidebar: false
@@ -127,8 +134,8 @@ export default [
127134
{
128135
path: '/log/:workflowName(.*)',
129136
view: 'Log',
130-
name: 'log',
131137
meta: {
138+
getTitle: workflowTitle,
132139
layout: 'default',
133140
toolbar: true,
134141
showSidebar: false
@@ -138,8 +145,8 @@ export default [
138145
{
139146
path: '/analysis/:workflowName(.*)',
140147
view: 'Analysis',
141-
name: 'analysis',
142148
meta: {
149+
getTitle: workflowTitle,
143150
layout: 'default',
144151
toolbar: true,
145152
showSidebar: false
@@ -149,8 +156,8 @@ export default [
149156
{
150157
path: '/gantt/:workflowName(.*)',
151158
view: 'Gantt',
152-
name: 'gantt',
153159
meta: {
160+
getTitle: workflowTitle,
154161
layout: 'default',
155162
toolbar: true,
156163
showSidebar: false
@@ -160,9 +167,9 @@ export default [
160167
{
161168
path: '/noAuth',
162169
view: 'NoAuth',
163-
name: 'noAuth',
164170
meta: {
171+
title: 'Unauthorized',
165172
layout: 'noAuth',
166173
},
167-
}
174+
},
168175
]

src/utils/index.js

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/**
1+
/*
22
* Copyright (C) NIWA & British Crown (Met Office) & Contributors.
33
*
44
* This program is free software: you can redistribute it and/or modify
@@ -16,18 +16,6 @@
1616
*/
1717

1818
import { watch } from 'vue'
19-
import { i18n } from '@/i18n'
20-
21-
/**
22-
* i18n-enabled operation, to get the title respecting the locale used
23-
* in the application settings.
24-
* @param {string} key - i18n key
25-
* @param {Object} params - optional object key=value used in the i18n message
26-
* @returns {string}
27-
*/
28-
export const getPageTitle = (key, params = {}) => {
29-
return `${i18n.global.t('App.name')} | ${i18n.global.t(key, params)}`
30-
}
3119

3220
/**
3321
* Watch source until it is truthy, then call the callback (and stop watching).

src/views/Analysis.vue

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ import {
144144
pick,
145145
} from 'lodash'
146146
import gql from 'graphql-tag'
147-
import { getPageTitle } from '@/utils/index'
148147
import graphqlMixin from '@/mixins/graphql'
149148
import {
150149
initialOptions,
@@ -248,12 +247,6 @@ export default {
248247
TimeSeries
249248
},
250249
251-
head () {
252-
return {
253-
title: getPageTitle('App.workflow', { name: this.workflowName })
254-
}
255-
},
256-
257250
beforeMount () {
258251
this.tasksQuery()
259252
},

0 commit comments

Comments
 (0)