@nextcloud/vue
Version:
Nextcloud vue components
573 lines (572 loc) • 20.3 kB
JavaScript
require('../assets/NcMentionBubble-C6t8od-_.css');
require('../assets/NcAvatar-5N7xP8zN.css');
"use strict";
const NcActions = require("./NcActions-gOMT5VzC.cjs");
const Components_NcActionLink = require("../Components/NcActionLink.cjs");
const Components_NcActionRouter = require("../Components/NcActionRouter.cjs");
const Components_NcActionText = require("../Components/NcActionText.cjs");
const Components_NcButton = require("../Components/NcButton.cjs");
const NcIconSvgWrapper = require("./NcIconSvgWrapper-Cb4bAwV6.cjs");
const Components_NcLoadingIcon = require("../Components/NcLoadingIcon.cjs");
const NcUserStatusIcon = require("./NcUserStatusIcon-FtKzp5sl.cjs");
const usernameToColor = require("./usernameToColor-u2MH9nY7.cjs");
const getAvatarUrl = require("./getAvatarUrl-6z9qRNH-.cjs");
require("../Composables/useIsFullscreen.cjs");
require("../Composables/useIsMobile.cjs");
;/* empty css */
const _pluginVue2_normalizer = require("./_plugin-vue2_normalizer-V0q-tHlQ.cjs");
require("linkify-string");
require("escape-html");
require("striptags");
require("vue");
const auth = require("@nextcloud/auth");
const axios = require("@nextcloud/axios");
const capabilities = require("@nextcloud/capabilities");
const router = require("@nextcloud/router");
const _l10n = require("./_l10n-Ci9_UR5q.cjs");
const autolink = require("./autolink-BAgL31EZ.cjs");
const DotsHorizontal = require("./DotsHorizontal-DQfnwpao.cjs");
const eventBus = require("@nextcloud/event-bus");
const browserStorage$1 = require("@nextcloud/browser-storage");
const components = require("@vueuse/components");
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
const axios__default = /* @__PURE__ */ _interopDefault(axios);
const userStatus = {
data() {
return {
hasStatus: false,
userStatus: {
status: null,
message: null,
icon: null
}
};
},
methods: {
/**
* Fetches the user-status from the server
*
* @param {string} userId UserId of the user to fetch the status for
*
* @return {Promise<void>}
*/
async fetchUserStatus(userId) {
var _a, _b;
if (!userId) {
return;
}
const capabilities$1 = capabilities.getCapabilities();
if (!Object.prototype.hasOwnProperty.call(capabilities$1, "user_status") || !capabilities$1.user_status.enabled) {
return;
}
if (!auth.getCurrentUser()) {
return;
}
try {
const { data } = await axios__default.default.get(router.generateOcsUrl("apps/user_status/api/v1/statuses/{userId}", { userId }));
const {
status,
message,
icon
} = data.ocs.data;
this.userStatus.status = status;
this.userStatus.message = message || "";
this.userStatus.icon = icon || "";
this.hasStatus = true;
} catch (e) {
if (e.response.status === 404 && ((_b = (_a = e.response.data.ocs) == null ? void 0 : _a.data) == null ? void 0 : _b.length) === 0) {
return;
}
console.error(e);
}
}
}
};
_l10n.register(_l10n.t10);
const browserStorage = browserStorage$1.getBuilder("nextcloud").persist().build();
function getUserHasAvatar(userId) {
const flag = browserStorage.getItem("user-has-avatar." + userId);
if (typeof flag === "string") {
return Boolean(flag);
}
return null;
}
function setUserHasAvatar(userId, flag) {
if (userId) {
browserStorage.setItem("user-has-avatar." + userId, flag);
}
}
const _sfc_main = {
name: "NcAvatar",
directives: {
ClickOutside: components.vOnClickOutside
},
components: {
DotsHorizontal: DotsHorizontal.DotsHorizontal,
NcActions: NcActions.NcActions,
NcButton: Components_NcButton,
NcIconSvgWrapper: NcIconSvgWrapper.NcIconSvgWrapper,
NcLoadingIcon: Components_NcLoadingIcon,
NcUserStatusIcon: NcUserStatusIcon.NcUserStatusIcon
},
mixins: [userStatus],
props: {
/**
* Set a custom url to the avatar image
* either the url, user or displayName property must be defined
*/
url: {
type: String,
default: void 0
},
/**
* Set a css icon-class for an icon to be used instead of the avatar.
*/
iconClass: {
type: String,
default: void 0
},
/**
* Set the user id to fetch the avatar
* either the url, user or displayName property must be defined
*/
user: {
type: String,
default: void 0
},
/**
* Whether or not to display the user-status
*/
showUserStatus: {
type: Boolean,
default: true
},
/**
* Whether or not to the status-icon should be used instead of online/away
*/
showUserStatusCompact: {
type: Boolean,
default: true
},
/**
* When the user status was preloaded via another source it can be handed in with this property to save the request.
* If this property is not set the status will be fetched automatically.
* If a preloaded no-status is available provide this object with properties "status", "icon" and "message" set to null.
*/
preloadedUserStatus: {
type: Object,
default: void 0
},
/**
* Is the user a guest user (then we have to user a different endpoint)
*/
isGuest: {
type: Boolean,
default: false
},
/**
* Set a display name that will be rendered as a tooltip
* either the url, user or displayName property must be defined
* specify just the displayname to generate a placeholder avatar without
* trying to fetch the avatar based on the user id
*/
displayName: {
type: String,
default: void 0
},
/**
* Set a size in px for the rendered avatar
*/
size: {
type: Number,
default: 32
},
/**
* Placeholder avatars will be automatically generated when this is set to true
*/
allowPlaceholder: {
type: Boolean,
default: true
},
/**
* Disable the tooltip
*/
disableTooltip: {
type: Boolean,
default: false
},
/**
* Disable the menu
*/
disableMenu: {
type: Boolean,
default: false
},
/**
* Declares a custom tooltip when not null
* Fallback will be the displayName
*
* requires disableTooltip not to be set to true
*/
tooltipMessage: {
type: String,
default: null
},
/**
* Declares username is not a user's name, when true.
* Prevents loading user's avatar from server and forces generating colored initials,
* i.e. if the user is a group
*/
isNoUser: {
type: Boolean,
default: false
},
/**
* Selector for the popover menu container
*/
menuContainer: {
type: [String, Object, Element, Boolean],
default: "body"
}
},
data() {
return {
avatarUrlLoaded: null,
avatarSrcSetLoaded: null,
userDoesNotExist: false,
isAvatarLoaded: false,
isMenuLoaded: false,
contactsMenuLoading: false,
contactsMenuActions: [],
contactsMenuOpenState: false
};
},
computed: {
avatarAriaLabel() {
var _a, _b;
if (!this.hasMenu) {
return;
}
if (this.canDisplayUserStatus || this.showUserStatusIconOnAvatar) {
return _l10n.t("Avatar of {displayName}, {status}", { displayName: (_a = this.displayName) != null ? _a : this.user, status: NcUserStatusIcon.getUserStatusText(this.userStatus.status) });
}
return _l10n.t("Avatar of {displayName}", { displayName: (_b = this.displayName) != null ? _b : this.user });
},
canDisplayUserStatus() {
return this.showUserStatus && this.hasStatus && ["online", "away", "busy", "dnd"].includes(this.userStatus.status);
},
showUserStatusIconOnAvatar() {
return this.showUserStatus && this.showUserStatusCompact && this.hasStatus && this.userStatus.status !== "dnd" && this.userStatus.icon;
},
/**
* The user identifier, either the display name if set or the user property
* If both properties are not set an empty string is returned
*/
userIdentifier() {
if (this.isDisplayNameDefined) {
return this.displayName;
}
if (this.isUserDefined) {
return this.user;
}
return "";
},
isUserDefined() {
return typeof this.user !== "undefined";
},
isDisplayNameDefined() {
return typeof this.displayName !== "undefined";
},
isUrlDefined() {
return typeof this.url !== "undefined";
},
hasMenu() {
var _a;
if (this.disableMenu) {
return false;
}
if (this.isMenuLoaded) {
return this.menu.length > 0;
}
return !(this.user === ((_a = auth.getCurrentUser()) == null ? void 0 : _a.uid) || this.userDoesNotExist || this.url);
},
/**
* True if initials should be shown as the user icon fallback
*/
showInitials() {
return this.allowPlaceholder && this.userDoesNotExist && !(this.iconClass || this.$slots.icon);
},
avatarStyle() {
const style = {
"--size": this.size + "px",
lineHeight: this.size + "px",
fontSize: Math.round(this.size * 0.45) + "px"
};
return style;
},
initialsWrapperStyle() {
const { r, g, b } = usernameToColor.usernameToColor(this.userIdentifier);
return {
backgroundColor: "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", 0.1)")
};
},
initialsStyle() {
const { r, g, b } = usernameToColor.usernameToColor(this.userIdentifier);
return {
color: "rgb(".concat(r, ", ").concat(g, ", ").concat(b, ")")
};
},
tooltip() {
if (this.disableTooltip) {
return false;
}
if (this.tooltipMessage) {
return this.tooltipMessage;
}
return this.displayName;
},
/**
* Get the (max. two) initials of the user as uppcase string
*/
initials() {
let initials = "?";
if (this.showInitials) {
const user = this.userIdentifier.trim();
if (user === "") {
return initials;
}
const filteredChars = user.match(/[\p{L}\p{N}\s]/gu);
if (filteredChars == null) {
return initials;
}
const filtered = filteredChars.join("");
const idx = filtered.lastIndexOf(" ");
initials = String.fromCodePoint(filtered.codePointAt(0));
if (idx !== -1) {
initials = initials.concat(String.fromCodePoint(filtered.codePointAt(idx + 1)));
}
}
return initials.toLocaleUpperCase();
},
menu() {
const actions = this.contactsMenuActions.map((item) => {
const route = autolink.getRoute(this.$router, item.hyperlink);
return {
ncActionComponent: route ? Components_NcActionRouter : Components_NcActionLink,
ncActionComponentProps: route ? {
to: route,
icon: item.icon
} : {
href: item.hyperlink,
icon: item.icon
},
text: item.title
};
});
function escape(html) {
const text = document.createTextNode(html);
const p = document.createElement("p");
p.appendChild(text);
return p.innerHTML;
}
if (this.showUserStatus && (this.userStatus.icon || this.userStatus.message)) {
const emojiIcon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">\n <text x="50%" y="50%" text-anchor="middle" style="dominant-baseline: central; font-size: 85%">'.concat(escape(this.userStatus.icon), "</text>\n </svg>");
return [{
ncActionComponent: Components_NcActionText,
ncActionComponentProps: {},
iconSvg: this.userStatus.icon ? emojiIcon : void 0,
text: "".concat(this.userStatus.message)
}].concat(actions);
}
return actions;
}
},
watch: {
url() {
this.userDoesNotExist = false;
this.loadAvatarUrl();
},
user() {
this.userDoesNotExist = false;
this.isMenuLoaded = false;
this.loadAvatarUrl();
}
},
mounted() {
this.loadAvatarUrl();
eventBus.subscribe("settings:avatar:updated", this.loadAvatarUrl);
eventBus.subscribe("settings:display-name:updated", this.loadAvatarUrl);
if (this.showUserStatus && this.user && !this.isNoUser) {
if (!this.preloadedUserStatus) {
this.fetchUserStatus(this.user);
} else {
this.userStatus.status = this.preloadedUserStatus.status || "";
this.userStatus.message = this.preloadedUserStatus.message || "";
this.userStatus.icon = this.preloadedUserStatus.icon || "";
this.hasStatus = this.preloadedUserStatus.status !== null;
}
eventBus.subscribe("user_status:status.updated", this.handleUserStatusUpdated);
}
},
beforeDestroy() {
eventBus.unsubscribe("settings:avatar:updated", this.loadAvatarUrl);
eventBus.unsubscribe("settings:display-name:updated", this.loadAvatarUrl);
if (this.showUserStatus && this.user && !this.isNoUser) {
eventBus.unsubscribe("user_status:status.updated", this.handleUserStatusUpdated);
}
},
methods: {
t: _l10n.t,
handleUserStatusUpdated(state) {
if (this.user === state.userId) {
this.userStatus = {
status: state.status,
icon: state.icon,
message: state.message
};
}
},
/**
* Toggle the popover menu on click or enter
* @param {KeyboardEvent|MouseEvent} event the UI event
*/
async toggleMenu(event) {
if (event.type === "keydown" && event.key !== "Enter") {
return;
}
if (!this.contactsMenuOpenState) {
await this.fetchContactsMenu();
}
this.contactsMenuOpenState = !this.contactsMenuOpenState;
},
closeMenu() {
this.contactsMenuOpenState = false;
},
async fetchContactsMenu() {
this.contactsMenuLoading = true;
try {
const user = encodeURIComponent(this.user);
const { data } = await axios__default.default.post(router.generateUrl("contactsmenu/findOne"), "shareType=0&shareWith=".concat(user));
this.contactsMenuActions = data.topAction ? [data.topAction].concat(data.actions) : data.actions;
} catch (e) {
this.contactsMenuOpenState = false;
}
this.contactsMenuLoading = false;
this.isMenuLoaded = true;
},
/**
* Handle avatar loading if user or url defined
*/
loadAvatarUrl() {
this.isAvatarLoaded = false;
if (!this.isUrlDefined && (!this.isUserDefined || this.isNoUser)) {
this.isAvatarLoaded = true;
this.userDoesNotExist = true;
return;
}
if (this.isUrlDefined) {
this.updateImageIfValid(this.url);
return;
}
if (this.size <= 64) {
const avatarUrl = this.avatarUrlGenerator(this.user, 64);
const srcset = [
avatarUrl + " 1x",
this.avatarUrlGenerator(this.user, 512) + " 8x"
].join(", ");
this.updateImageIfValid(avatarUrl, srcset);
} else {
const avatarUrl = this.avatarUrlGenerator(this.user, 512);
this.updateImageIfValid(avatarUrl);
}
},
/**
* Generate an avatar url from the server's avatar endpoint
*
* @param {string} user the user id
* @param {number} size the desired size
* @return {string}
*/
avatarUrlGenerator(user, size) {
var _a;
let avatarUrl = getAvatarUrl.getAvatarUrl(user, size, this.isGuest);
if (user === ((_a = auth.getCurrentUser()) == null ? void 0 : _a.uid) && typeof oc_userconfig !== "undefined") {
avatarUrl += "?v=" + oc_userconfig.avatar.version;
}
return avatarUrl;
},
/**
* Check if the provided url is valid and update Avatar if so
*
* @param {string} url the avatar url
* @param {Array} srcset the avatar srcset
*/
updateImageIfValid(url, srcset = null) {
const userHasAvatar = getUserHasAvatar(this.user);
if (this.isUserDefined && typeof userHasAvatar === "boolean") {
this.isAvatarLoaded = true;
this.avatarUrlLoaded = url;
if (srcset) {
this.avatarSrcSetLoaded = srcset;
}
if (userHasAvatar === false) {
this.userDoesNotExist = true;
}
return;
}
const img = new Image();
img.onload = () => {
this.avatarUrlLoaded = url;
if (srcset) {
this.avatarSrcSetLoaded = srcset;
}
this.isAvatarLoaded = true;
setUserHasAvatar(this.user, true);
};
img.onerror = () => {
console.debug("Invalid avatar url", url);
this.avatarUrlLoaded = null;
this.avatarSrcSetLoaded = null;
this.userDoesNotExist = true;
this.isAvatarLoaded = false;
setUserHasAvatar(this.user, false);
};
if (srcset) {
img.srcset = srcset;
}
img.src = url;
}
}
};
var _sfc_render = function render() {
var _vm = this, _c = _vm._self._c;
return _c("span", { directives: [{ name: "click-outside", rawName: "v-click-outside", value: _vm.closeMenu, expression: "closeMenu" }], ref: "main", staticClass: "avatardiv popovermenu-wrapper", class: {
"avatardiv--unknown": _vm.userDoesNotExist,
"avatardiv--with-menu": _vm.hasMenu,
"avatardiv--with-menu-loading": _vm.contactsMenuLoading
}, style: _vm.avatarStyle }, [_vm._t("icon", function() {
return [_vm.iconClass ? _c("span", { staticClass: "avatar-class-icon", class: _vm.iconClass }) : _vm.isAvatarLoaded && !_vm.userDoesNotExist ? _c("img", { attrs: { "src": _vm.avatarUrlLoaded, "srcset": _vm.avatarSrcSetLoaded, "alt": "" } }) : _vm._e()];
}), _vm.hasMenu && _vm.menu.length === 0 ? _c("NcButton", { staticClass: "action-item action-item__menutoggle", attrs: { "type": "tertiary-no-background", "aria-label": _vm.avatarAriaLabel, "title": _vm.tooltip }, on: { "click": _vm.toggleMenu }, scopedSlots: _vm._u([{ key: "icon", fn: function() {
return [_vm.contactsMenuLoading ? _c("NcLoadingIcon") : _c("DotsHorizontal", { attrs: { "size": 20 } })];
}, proxy: true }], null, false, 2617833509) }) : _vm.hasMenu ? _c("NcActions", { attrs: { "force-menu": "", "manual-open": "", "type": "tertiary-no-background", "container": _vm.menuContainer, "open": _vm.contactsMenuOpenState, "aria-label": _vm.avatarAriaLabel, "title": _vm.tooltip }, on: { "update:open": function($event) {
_vm.contactsMenuOpenState = $event;
}, "click": _vm.toggleMenu }, scopedSlots: _vm._u([_vm.contactsMenuLoading ? { key: "icon", fn: function() {
return [_c("NcLoadingIcon")];
}, proxy: true } : null], null, true) }, _vm._l(_vm.menu, function(item, key) {
return _c(item.ncActionComponent, _vm._b({ key, tag: "component", scopedSlots: _vm._u([item.iconSvg ? { key: "icon", fn: function() {
return [_c("NcIconSvgWrapper", { attrs: { "svg": item.iconSvg } })];
}, proxy: true } : null], null, true) }, "component", item.ncActionComponentProps, false), [_vm._v(" " + _vm._s(item.text) + " ")]);
}), 1) : _vm._e(), _vm.showUserStatusIconOnAvatar ? _c("span", { staticClass: "avatardiv__user-status avatardiv__user-status--icon" }, [_vm._v(" " + _vm._s(_vm.userStatus.icon) + " ")]) : _vm.canDisplayUserStatus ? _c("NcUserStatusIcon", { staticClass: "avatardiv__user-status", attrs: { "status": _vm.userStatus.status, "aria-hidden": String(_vm.hasMenu) } }) : _vm._e(), _vm.showInitials ? _c("span", { staticClass: "avatardiv__initials-wrapper", style: _vm.initialsWrapperStyle }, [_c("span", { staticClass: "avatardiv__initials", style: _vm.initialsStyle }, [_vm._v(" " + _vm._s(_vm.initials) + " ")])]) : _vm._e()], 2);
};
var _sfc_staticRenderFns = [];
var __component__ = /* @__PURE__ */ _pluginVue2_normalizer.normalizeComponent(
_sfc_main,
_sfc_render,
_sfc_staticRenderFns,
false,
null,
"e7e86f59"
);
const NcAvatar = __component__.exports;
exports.NcAvatar = NcAvatar;
exports.userStatus = userStatus;