Skip to content

Commit 8310da7

Browse files
committed
feat(NcAvatar): implement custom javascript hook action for the contacts menu
Signed-off-by: Richard Steinmetz <richard@steinmetz.cloud>
1 parent 30a542d commit 8310da7

File tree

4 files changed

+91
-0
lines changed

4 files changed

+91
-0
lines changed

src/components/NcAvatar/NcAvatar.vue

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,12 +242,15 @@ import NcButton from '../NcButton/index.ts'
242242
import NcIconSvgWrapper from '../NcIconSvgWrapper/index.js'
243243
import NcLoadingIcon from '../NcLoadingIcon/index.js'
244244
import NcUserStatusIcon from '../NcUserStatusIcon/index.js'
245+
import NcActionButton from '../NcActionButton/index.js'
245246
import usernameToColor from '../../functions/usernameToColor/index.js'
247+
import { getEnabledContactsMenuActions } from '../../functions/contactsMenu/index.ts'
246248
import { getAvatarUrl } from '../../utils/getAvatarUrl.ts'
247249
import { getUserStatusText } from '../../utils/UserStatus.ts'
248250
import { userStatus } from '../../mixins/index.js'
249251
import { t } from '../../l10n.js'
250252
import { getRoute } from '../../components/NcRichText/autolink.ts'
253+
import logger from '../../utils/logger.ts'
251254
252255
import axios from '@nextcloud/axios'
253256
import DotsHorizontal from 'vue-material-design-icons/DotsHorizontal.vue'
@@ -425,6 +428,7 @@ export default {
425428
isAvatarLoaded: false,
426429
isMenuLoaded: false,
427430
contactsMenuLoading: false,
431+
contactsMenuData: {},
428432
contactsMenuActions: [],
429433
contactsMenuOpenState: false,
430434
}
@@ -571,6 +575,24 @@ export default {
571575
}
572576
})
573577
578+
for (const action of getEnabledContactsMenuActions(this.contactsMenuData)) {
579+
try {
580+
actions.push({
581+
ncActionComponent: NcActionButton,
582+
ncActionComponentProps: {
583+
onClick: () => action.callback(this.contactsMenuData),
584+
},
585+
text: action.displayName(this.contactsMenuData),
586+
iconSvg: action.iconSvg(this.contactsMenuData),
587+
})
588+
} catch (error) {
589+
logger.error(`Failed to render ContactsMenu action ${action.id}`, {
590+
error,
591+
action,
592+
})
593+
}
594+
}
595+
574596
/**
575597
* @param {string} html The HTML to escape
576598
*/
@@ -668,6 +690,7 @@ export default {
668690
try {
669691
const user = encodeURIComponent(this.user)
670692
const { data } = await axios.post(generateUrl('contactsmenu/findOne'), `shareType=0&shareWith=${user}`)
693+
this.contactsMenuData = data
671694
this.contactsMenuActions = data.topAction ? [data.topAction].concat(data.actions) : data.actions
672695
} catch (e) {
673696
this.contactsMenuOpenState = false
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import logger from '../../utils/logger.ts'
7+
8+
// Taken from \OC\Contacts\ContactsMenu\Entry::jsonSerialize
9+
export interface ContactsMenuEntry {
10+
id: number | string | null
11+
fullName: string
12+
avatar: string | null
13+
topAction: object | null
14+
actions: object[]
15+
lastMessage: string
16+
emailAddresses: string[]
17+
profileTitle: string | null
18+
profileUrl: string | null
19+
status: string | null
20+
statusMessage: string | null
21+
statusMessageTimestamp: number | null
22+
statusIcon: string | null
23+
isUser: boolean
24+
uid: string | null
25+
}
26+
27+
export interface ContactsMenuAction {
28+
id: string
29+
displayName: (entry: ContactsMenuEntry) => string
30+
enabled: (entry: ContactsMenuEntry) => boolean
31+
iconSvg: (entry: ContactsMenuEntry) => string
32+
callback: (entry: ContactsMenuEntry) => void
33+
}
34+
35+
/**
36+
* Register a contacts and avatar menu action that will invoke the given callback on click.
37+
*
38+
* @param action - The action to register
39+
*/
40+
export function registerContactsMenuAction(action: ContactsMenuAction): void {
41+
window._nc_contacts_menu_hooks ??= {}
42+
43+
if (window._nc_contacts_menu_hooks[action.id]) {
44+
logger.error(`ContactsMenu action for id ${action.id} has already been registered`, {
45+
action,
46+
})
47+
return
48+
}
49+
50+
window._nc_contacts_menu_hooks[action.id] = action
51+
}
52+
53+
/**
54+
* Get all registered and enabled contacts menu actions for the given menu entry.
55+
*
56+
* @param entry - The contacts menu entry object as returned by the backend
57+
*/
58+
export function getEnabledContactsMenuActions(entry: ContactsMenuEntry): ContactsMenuAction[] {
59+
if (!window._nc_contacts_menu_hooks) {
60+
return []
61+
}
62+
63+
return Object.values(window._nc_contacts_menu_hooks).filter((action) => action.enabled(entry))
64+
}

src/functions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
export * from './a11y/index.ts'
7+
export * from './contactsMenu/index.ts'
78
export * from './dialog/index.ts'
89
export * from './emoji/index.ts'
910
export * from './isDarkTheme/index.ts'

src/globals.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
55

6+
import type { ContactsMenuAction } from './functions/contactsMenu/index.ts'
7+
68
declare const PRODUCTION: boolean
79
declare const SCOPE_VERSION: string
810

@@ -23,6 +25,7 @@ declare global {
2325
OCP: any
2426
// internal global variables
2527
_nc_vue_element_id?: number
28+
_nc_contacts_menu_hooks: { [id: string]: ContactsMenuAction },
2629
}
2730
}
2831

0 commit comments

Comments
 (0)