Skip to content

Commit 23ff726

Browse files
committed
refactor: replace JavaScript navigation with links
Assisted-by: Codex:gpt-5 Signed-off-by: Abhinav Ohri <abhinavohri13@gmail.com>
1 parent e047346 commit 23ff726

9 files changed

Lines changed: 101 additions & 139 deletions

File tree

src/components/AppNavigation/Proposal/ProposalList.vue

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020

2121
<template v-if="!userHasEmailAddress">
2222
<NcAppNavigationItem
23-
:name="t('calendar', 'A configured email address is required to use meeting proposals')"
24-
@click="window.open(generateUrl('settings/user'), '_blank').focus()">
23+
:href="userSettingsUrl"
24+
:name="t('calendar', 'A configured email address is required to use meeting proposals')">
2525
<template #icon>
2626
<WarningIcon :size="20" class="proposal-list__warning-icon" />
2727
</template>
@@ -97,7 +97,7 @@ import type { Proposal } from '@/models/proposals/proposals'
9797
9898
import { showError, showSuccess } from '@nextcloud/dialogs'
9999
import { t } from '@nextcloud/l10n'
100-
import { generateUrl } from '@nextcloud/router'
100+
import { generateUrl, getBaseUrl } from '@nextcloud/router'
101101
import { computed, onMounted, ref, watch } from 'vue'
102102
// icons
103103
import WarningIcon from 'vue-material-design-icons/AlertCircleOutline'
@@ -127,6 +127,7 @@ const showDeleteDialog = ref(false)
127127
const pendingDeleteProposal = ref<Proposal | null>(null)
128128
129129
const userHasEmailAddress = computed(() => (principalStore?.getCurrentUserPrincipal?.emailAddress?.length ?? 0) > 0)
130+
const userSettingsUrl = generateUrl('settings/user', {}, { baseURL: getBaseUrl() })
130131
131132
const deleteDialogMessage = computed(() => {
132133
const title = pendingDeleteProposal.value?.title ?? t('calendar', 'No title')

src/components/AppointmentConfigModal/NoEmailAddressWarning.vue

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,18 @@
55

66
<script setup lang="ts">
77
import { t } from '@nextcloud/l10n'
8-
import { generateUrl } from '@nextcloud/router'
8+
import { generateUrl, getBaseUrl } from '@nextcloud/router'
99
import { NcAppNavigationItem } from '@nextcloud/vue'
1010
import AlertCircleIcon from 'vue-material-design-icons/AlertCircle.vue'
1111
1212
const title = t('calendar', 'To configure appointments, add your email address in personal settings.')
13-
14-
function openUserSettings(): void {
15-
const url = generateUrl('settings/user')
16-
window.open(url, '_blank')?.focus()
17-
}
13+
const userSettingsUrl = generateUrl('settings/user', {}, { baseURL: getBaseUrl() })
1814
</script>
1915

2016
<template>
2117
<NcAppNavigationItem
22-
:name="title"
23-
@click="openUserSettings">
18+
:href="userSettingsUrl"
19+
:name="title">
2420
<template #icon>
2521
<AlertCircleIcon :size="20" class="no-email-warning__icon" />
2622
</template>

src/components/Editor/Attachments/AttachmentsList.vue

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,22 @@
7070
<NcDialog
7171
v-if="showOpenConfirmation"
7272
v-model:open="showOpenConfirmation"
73-
:name="t('calendar', 'Confirmation')"
74-
:buttons="openConfirmationButtons">
73+
:name="t('calendar', 'Confirmation')">
7574
<p class="external-link-message">
7675
{{ openConfirmationMessage }}
7776
</p>
77+
<template #actions>
78+
<NcButton @click="showOpenConfirmation = false">
79+
{{ t('calendar', 'Cancel') }}
80+
</NcButton>
81+
<NcButton
82+
:href="openConfirmationUrl"
83+
target="_blank"
84+
rel="noopener noreferrer"
85+
variant="primary">
86+
{{ t('calendar', 'Proceed') }}
87+
</NcButton>
88+
</template>
7889
</NcDialog>
7990
</div>
8091
</template>
@@ -85,6 +96,7 @@ import { generateUrl, getBaseUrl } from '@nextcloud/router'
8596
import {
8697
NcActionButton,
8798
NcActions,
99+
NcButton,
88100
NcDialog,
89101
NcListItem,
90102
} from '@nextcloud/vue'
@@ -110,6 +122,7 @@ export default {
110122
NcListItem,
111123
NcActions,
112124
NcActionButton,
125+
NcButton,
113126
Upload,
114127
Close,
115128
Folder,
@@ -130,7 +143,7 @@ export default {
130143
uploading: false,
131144
showOpenConfirmation: false,
132145
openConfirmationMessage: '',
133-
openConfirmationButtons: [],
146+
openConfirmationUrl: '',
134147
}
135148
},
136149
@@ -276,21 +289,7 @@ export default {
276289
*/
277290
showConfirmationDialog(message, url) {
278291
this.openConfirmationMessage = message
279-
this.openConfirmationButtons = [
280-
{
281-
label: t('calendar', 'Cancel'),
282-
callback: () => {
283-
this.showOpenConfirmation = false
284-
},
285-
},
286-
{
287-
label: t('calendar', 'Proceed'),
288-
type: 'primary',
289-
callback: () => {
290-
window.open(url.href, '_blank', 'noopener noreferrer')
291-
},
292-
},
293-
]
292+
this.openConfirmationUrl = url.href
294293
this.showOpenConfirmation = true
295294
},
296295
},

src/fullcalendar/eventSources/eventSourceFunction.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
getHexForColorName,
1212
} from '@/utils/color.js'
1313
import logger from '@/utils/logger.js'
14+
import { generateTaskUrl } from '@/utils/url.ts'
1415
/**
1516
* convert an array of calendar-objects to events
1617
*
@@ -197,6 +198,10 @@ export function eventSourceFunction(calendarObjects, calendar, start, end, timez
197198
},
198199
}
199200

201+
if (object.name === 'VTODO' && settingsStore.tasksEnabled) {
202+
fcEvent.url = generateTaskUrl(calendarObject.dav.url)
203+
}
204+
200205
if (object.color) {
201206
const customColor = getHexForColorName(object.color)
202207
if (customColor) {

src/fullcalendar/interaction/eventClick.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import { showInfo } from '@nextcloud/dialogs'
66
import { emit } from '@nextcloud/event-bus'
77
import { translate as t } from '@nextcloud/l10n'
8-
import { generateUrl } from '@nextcloud/router'
98
import { errorCatchAsync } from '@/fullcalendar/utils/errors.js'
109
import useSettingsStore from '@/store/settings.js'
1110
import useWidgetStore from '@/store/widget.js'
@@ -28,7 +27,7 @@ import {
2827
*/
2928
export default function(router, route, window, isWidget = false, ref = undefined) {
3029
const widgetStore = useWidgetStore()
31-
return errorCatchAsync(function({ event }) {
30+
return errorCatchAsync(function({ event, jsEvent }) {
3231
if (isWidget) {
3332
widgetStore.setWidgetRef({ widgetRef: ref.fullCalendar.$el })
3433
}
@@ -38,7 +37,7 @@ export default function(router, route, window, isWidget = false, ref = undefined
3837
break
3938

4039
case 'VTODO':
41-
handleToDoClick(event, route, window, isWidget)
40+
handleToDoClick(event, route, jsEvent, isWidget)
4241
break
4342
}
4443
}, 'eventClick')
@@ -94,13 +93,14 @@ function handleEventClick(event, router, route, window, isWidget = false) {
9493
*
9594
* @param {EventDef} event FullCalendar event
9695
* @param {object} route The current Vue route
97-
* @param {Window} window The window object
96+
* @param {MouseEvent} jsEvent The native click event
9897
* @param {boolean} isWidget Whether the calendar is embedded as a widget
9998
*/
100-
function handleToDoClick(event, route, window, isWidget = false) {
99+
function handleToDoClick(event, route, jsEvent, isWidget = false) {
101100
const settingsStore = useSettingsStore()
102101

103102
if (getViewMode(route.name, isWidget) !== ViewMode.USER) {
103+
jsEvent?.preventDefault()
104104
return
105105
}
106106

@@ -111,9 +111,8 @@ function handleToDoClick(event, route, window, isWidget = false) {
111111
emit('calendar:handle-todo-click', { calendarId, taskId })
112112

113113
if (!settingsStore.tasksEnabled) {
114+
jsEvent?.preventDefault()
114115
showInfo(t('calendar', 'Please ask your administrator to enable the Tasks App.'))
115116
return
116117
}
117-
const url = `apps/tasks/calendars/${encodeURIComponent(calendarId)}/tasks/${encodeURIComponent(taskId)}`
118-
window.open(generateUrl(url), '_blank')
119118
}

src/fullcalendar/rendering/eventDidMount.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ function buildAriaLabel(event) {
133133
export default errorCatch(function({ event, el }) {
134134
// Set aria-label for screen reader accessibility
135135
el.setAttribute('aria-label', buildAriaLabel(event))
136+
137+
if (event.extendedProps.objectType === 'VTODO' && event.url) {
138+
const link = el.matches('a') ? el : el.querySelector('a[href]')
139+
link?.setAttribute('target', '_blank')
140+
link?.setAttribute('rel', 'noopener noreferrer')
141+
el.classList.remove('fc-event-forced-url')
142+
}
136143
if (el.classList.contains('fc-event-nc-alarms')) {
137144
const notificationIcon = document.createElement('span')
138145
notificationIcon.classList.add('icon-event-reminder')

src/utils/url.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
5+
import { generateUrl } from '@nextcloud/router'
6+
7+
/**
8+
* Generate the Tasks app URL for a calendar task.
9+
*
10+
* @param davUrl - The task's DAV URL
11+
* @return The task URL
12+
*/
13+
export function generateTaskUrl(davUrl: string): string {
14+
const davUrlParts = davUrl.split('/')
15+
const taskId = davUrlParts.pop()!
16+
const calendarId = davUrlParts.pop()!
17+
18+
return generateUrl(`apps/tasks/calendars/${encodeURIComponent(calendarId)}/tasks/${encodeURIComponent(taskId)}`)
19+
}
520

621
/**
722
* Works like urldecode() from php

0 commit comments

Comments
 (0)