mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-03 12:36:02 +01:00
Chat notifs (#11832)
* first part of chat notifs * moved default notif to manage page * spec * notifs * delete settings on room delete
This commit is contained in:
committed by
GitHub
parent
f377650161
commit
61f036ce1d
@@ -97,14 +97,14 @@
|
||||
"nodebb-plugin-emoji": "5.1.3",
|
||||
"nodebb-plugin-emoji-android": "4.0.0",
|
||||
"nodebb-plugin-markdown": "12.1.7",
|
||||
"nodebb-plugin-mentions": "4.3.2",
|
||||
"nodebb-plugin-mentions": "4.3.3",
|
||||
"nodebb-plugin-ntfy": "1.1.0",
|
||||
"nodebb-plugin-spam-be-gone": "2.1.1",
|
||||
"nodebb-rewards-essentials": "0.2.3",
|
||||
"nodebb-theme-harmony": "1.1.13",
|
||||
"nodebb-theme-harmony": "1.1.14",
|
||||
"nodebb-theme-lavender": "7.1.3",
|
||||
"nodebb-theme-peace": "2.1.3",
|
||||
"nodebb-theme-persona": "13.2.6",
|
||||
"nodebb-theme-persona": "13.2.7",
|
||||
"nodebb-widget-essentials": "7.0.13",
|
||||
"nodemailer": "6.9.4",
|
||||
"nprogress": "0.2.0",
|
||||
|
||||
@@ -36,6 +36,12 @@
|
||||
"chat.public.groups-help": "To create a chat room that is visible to all users select registered-users from the group list.",
|
||||
"chat.manage-room": "Manage Chat Room",
|
||||
"chat.add-user": "Add User",
|
||||
"chat.notification-settings": "Notification Settings",
|
||||
"chat.default-notification-setting": "Default Notification Setting",
|
||||
"chat.notification-setting-room-default": "Room Default",
|
||||
"chat.notification-setting-none": "No notifications",
|
||||
"chat.notification-setting-at-mention-only": "@mention only",
|
||||
"chat.notification-setting-all-messages": "All messages",
|
||||
"chat.select-groups": "Select Groups",
|
||||
"chat.add-user-help": "Search for users here. When selected, the user will be added to the chat. The new user will not be able to see chat messages written before they were added to the conversation. Only room owners (<i class=\"fa fa-star text-warning\"></i>) may remove users from chat rooms.",
|
||||
"chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?",
|
||||
|
||||
@@ -22,6 +22,8 @@ RoomObject:
|
||||
timestamp:
|
||||
type: number
|
||||
description: Timestamp of when room was created
|
||||
notificationSetting:
|
||||
type: number
|
||||
MessageObject:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@@ -39,6 +39,12 @@ get:
|
||||
timestamp:
|
||||
type: number
|
||||
description: Timestamp of when room was created
|
||||
notificationSetting:
|
||||
type: number
|
||||
notificationOptions:
|
||||
type: array
|
||||
notificationOptionsIcon:
|
||||
type: string
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
@@ -318,6 +324,8 @@ get:
|
||||
type: string
|
||||
chatWithMessage:
|
||||
type: string
|
||||
notificationSetting:
|
||||
type: number
|
||||
publicRooms:
|
||||
type: array
|
||||
items:
|
||||
|
||||
@@ -99,6 +99,8 @@ define('forum/chats', [
|
||||
});
|
||||
userList.init(roomId, mainWrapper);
|
||||
Chats.addPublicRoomSortHandler();
|
||||
Chats.addTooltipHandler();
|
||||
Chats.addNotificationSettingHandler();
|
||||
};
|
||||
|
||||
Chats.addPublicRoomSortHandler = function () {
|
||||
@@ -122,6 +124,37 @@ define('forum/chats', [
|
||||
}
|
||||
};
|
||||
|
||||
Chats.addTooltipHandler = function () {
|
||||
$('[data-manual-tooltip]').tooltip({
|
||||
trigger: 'manual',
|
||||
animation: false,
|
||||
placement: 'bottom',
|
||||
}).on('mouseenter', function (ev) {
|
||||
const target = $(ev.target);
|
||||
const isDropdown = target.hasClass('dropdown-menu') || !!target.parents('.dropdown-menu').length;
|
||||
if (!isDropdown) {
|
||||
$(this).tooltip('show');
|
||||
}
|
||||
}).on('click mouseleave', function () {
|
||||
$(this).tooltip('hide');
|
||||
});
|
||||
};
|
||||
|
||||
Chats.addNotificationSettingHandler = function () {
|
||||
const notifSettingEl = $('[component="chat/notification/setting"]');
|
||||
|
||||
notifSettingEl.find('[data-value]').on('click', async function () {
|
||||
notifSettingEl.find('i.fa-check').addClass('hidden');
|
||||
const $this = $(this);
|
||||
$this.find('i.fa-check').removeClass('hidden');
|
||||
$('[component="chat/notification/setting/icon"]').attr('class', `fa ${$this.attr('data-icon')}`);
|
||||
await socket.emit('modules.chats.setNotificationSetting', {
|
||||
roomId: ajaxify.data.roomId,
|
||||
value: $this.attr('data-value'),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Chats.addUploadHandler = function (options) {
|
||||
uploadHelpers.init({
|
||||
dragDropAreaEl: options.dragDropAreaEl,
|
||||
|
||||
@@ -23,7 +23,7 @@ define('forum/chats/manage', [
|
||||
const html = await app.parseAndTranslate('modals/manage-room', {
|
||||
groups,
|
||||
user: app.user,
|
||||
group: ajaxify.data,
|
||||
room: ajaxify.data,
|
||||
});
|
||||
modal = bootbox.dialog({
|
||||
title: '[[modules:chat.manage-room]]',
|
||||
@@ -67,14 +67,28 @@ define('forum/chats/manage', [
|
||||
});
|
||||
});
|
||||
|
||||
modal.find('[component="chat/manage/save/groups"]').on('click', (ev) => {
|
||||
const btn = $(ev.target);
|
||||
modal.find('[component="chat/manage/save"]').on('click', () => {
|
||||
const notifSettingEl = modal.find('[component="chat/room/notification/setting"]');
|
||||
api.put(`/chats/${roomId}`, {
|
||||
groups: modal.find('[component="chat/room/groups"]').val(),
|
||||
notificationSetting: notifSettingEl.val(),
|
||||
}).then((payload) => {
|
||||
ajaxify.data.groups = payload.groups;
|
||||
btn.addClass('btn-success');
|
||||
setTimeout(() => btn.removeClass('btn-success'), 1000);
|
||||
ajaxify.data.notificationSetting = payload.notificationSetting;
|
||||
const roomDefaultOption = payload.notificationOptions[0];
|
||||
$('[component="chat/notification/setting"] [data-icon]').first().attr(
|
||||
'data-icon', roomDefaultOption.icon
|
||||
);
|
||||
$('[component="chat/notification/setting/sub-label"]').translateText(
|
||||
roomDefaultOption.subLabel
|
||||
);
|
||||
if (roomDefaultOption.selected) {
|
||||
$('[component="chat/notification/setting/icon"]').attr(
|
||||
'class', `fa ${roomDefaultOption.icon}`
|
||||
);
|
||||
}
|
||||
|
||||
modal.modal('hide');
|
||||
}).catch(alerts.error);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,11 +40,13 @@ chatsAPI.create = async function (caller, data) {
|
||||
if (!data) {
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
|
||||
const isPublic = data.type === 'public';
|
||||
const isAdmin = await user.isAdministrator(caller.uid);
|
||||
if (isPublic && !isAdmin) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
|
||||
if (!data.uids || !Array.isArray(data.uids)) {
|
||||
throw new Error(`[[error:wrong-parameter-type, uids, ${typeof data.uids}, Array]]`);
|
||||
}
|
||||
@@ -55,6 +57,11 @@ chatsAPI.create = async function (caller, data) {
|
||||
if (isPublic && (!Array.isArray(data.groups) || !data.groups.length)) {
|
||||
throw new Error('[[error:no-groups-selected]]');
|
||||
}
|
||||
|
||||
data.notificationSetting = isPublic ?
|
||||
messaging.notificationSettings.ATMENTION :
|
||||
messaging.notificationSettings.ALLMESSAGES;
|
||||
|
||||
await Promise.all(data.uids.map(async uid => messaging.canMessageUser(caller.uid, uid)));
|
||||
const roomId = await messaging.newRoom(caller.uid, data);
|
||||
|
||||
@@ -108,7 +115,6 @@ chatsAPI.update = async (caller, data) => {
|
||||
});
|
||||
}
|
||||
}
|
||||
if (data.hasOwnProperty('groups')) {
|
||||
const [roomData, isAdmin] = await Promise.all([
|
||||
messaging.getRoomData(data.roomId),
|
||||
user.isAdministrator(caller.uid),
|
||||
@@ -116,10 +122,14 @@ chatsAPI.update = async (caller, data) => {
|
||||
if (!roomData) {
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
if (data.hasOwnProperty('groups')) {
|
||||
if (roomData.public && isAdmin) {
|
||||
await db.setObjectField(`chat:room:${data.roomId}`, 'groups', JSON.stringify(data.groups));
|
||||
}
|
||||
}
|
||||
if (data.hasOwnProperty('notificationSetting') && isAdmin) {
|
||||
await db.setObjectField(`chat:room:${data.roomId}`, 'notificationSetting', data.notificationSetting);
|
||||
}
|
||||
return messaging.loadRoom(caller.uid, {
|
||||
roomId: data.roomId,
|
||||
});
|
||||
|
||||
@@ -25,6 +25,11 @@ require('./rooms')(Messaging);
|
||||
require('./unread')(Messaging);
|
||||
require('./notifications')(Messaging);
|
||||
|
||||
Messaging.notificationSettings = Object.create(null);
|
||||
Messaging.notificationSettings.NONE = 1;
|
||||
Messaging.notificationSettings.ATMENTION = 2;
|
||||
Messaging.notificationSettings.ALLMESSAGES = 3;
|
||||
|
||||
Messaging.messageExists = async mid => db.exists(`message:${mid}`);
|
||||
|
||||
Messaging.getMessages = async (params) => {
|
||||
|
||||
@@ -12,6 +12,23 @@ const meta = require('../meta');
|
||||
module.exports = function (Messaging) {
|
||||
// Only used to notify a user of a new chat message
|
||||
Messaging.notifyQueue = {};
|
||||
|
||||
Messaging.setUserNotificationSetting = async (uid, roomId, value) => {
|
||||
if (parseInt(value, 10) === -1) {
|
||||
// go back to default
|
||||
return await db.deleteObjectField(`chat:room:${roomId}:notification:settings`, uid);
|
||||
}
|
||||
await db.setObjectField(`chat:room:${roomId}:notification:settings`, uid, parseInt(value, 10));
|
||||
};
|
||||
|
||||
Messaging.getUidsNotificationSetting = async (uids, roomId) => {
|
||||
const [settings, roomData] = await Promise.all([
|
||||
db.getObjectFields(`chat:room:${roomId}:notification:settings`, uids),
|
||||
Messaging.getRoomData(roomId, ['notificationSetting']),
|
||||
]);
|
||||
return uids.map(uid => parseInt(settings[uid] || roomData.notificationSetting, 10));
|
||||
};
|
||||
|
||||
Messaging.notifyUsersInRoom = async (fromUid, roomId, messageObj) => {
|
||||
const isPublic = parseInt(await db.getObjectField(`chat:room:${roomId}`, 'public'), 10) === 1;
|
||||
|
||||
@@ -34,13 +51,15 @@ module.exports = function (Messaging) {
|
||||
// delivers unread public msg to all online users on the chats page
|
||||
io.in(`chat_room_public_${roomId}`).emit('event:chats.public.unread', unreadData);
|
||||
}
|
||||
if (messageObj.system || isPublic) {
|
||||
if (messageObj.system) {
|
||||
return;
|
||||
}
|
||||
|
||||
// push unread count only for private rooms
|
||||
if (!isPublic) {
|
||||
const uids = await Messaging.getAllUidsInRoomFromSet(`chat:room:${roomId}:uids:online`);
|
||||
Messaging.pushUnreadCount(uids, unreadData);
|
||||
}
|
||||
|
||||
// Delayed notifications
|
||||
let queueObj = Messaging.notifyQueue[`${fromUid}:${roomId}`];
|
||||
@@ -65,6 +84,29 @@ module.exports = function (Messaging) {
|
||||
};
|
||||
|
||||
async function sendNotification(fromUid, roomId, messageObj) {
|
||||
fromUid = parseInt(fromUid, 10);
|
||||
|
||||
const [settings, roomData] = await Promise.all([
|
||||
db.getObject(`chat:room:${roomId}:notification:settings`),
|
||||
Messaging.getRoomData(roomId, ['notificationSetting']),
|
||||
]);
|
||||
const roomDefault = roomData.notificationSetting;
|
||||
const uidsToNotify = [];
|
||||
const { ALLMESSAGES } = Messaging.notificationSettings;
|
||||
await batch.processSortedSet(`chat:room:${roomId}:uids:online`, async (uids) => {
|
||||
uids = uids.filter(
|
||||
uid => (parseInt((settings && settings[uid]) || roomDefault, 10) === ALLMESSAGES) &&
|
||||
fromUid !== parseInt(uid, 10)
|
||||
);
|
||||
const hasRead = await Messaging.hasRead(uids, roomId);
|
||||
uidsToNotify.push(...uids.filter((uid, index) => !hasRead[index]));
|
||||
}, {
|
||||
reverse: true,
|
||||
batch: 500,
|
||||
interval: 100,
|
||||
});
|
||||
|
||||
if (uidsToNotify.length) {
|
||||
const { displayname } = messageObj.fromUser;
|
||||
const isGroupChat = await Messaging.isGroupChat(roomId);
|
||||
const notification = await notifications.create({
|
||||
@@ -76,16 +118,7 @@ module.exports = function (Messaging) {
|
||||
from: fromUid,
|
||||
path: `/chats/${messageObj.roomId}`,
|
||||
});
|
||||
|
||||
await batch.processSortedSet(`chat:room:${roomId}:uids:online`, async (uids) => {
|
||||
const hasRead = await Messaging.hasRead(uids, roomId);
|
||||
uids = uids.filter((uid, index) => !hasRead[index] && parseInt(fromUid, 10) !== parseInt(uid, 10));
|
||||
|
||||
notifications.push(notification, uids);
|
||||
}, {
|
||||
reverse: true,
|
||||
batch: 500,
|
||||
interval: 1000,
|
||||
});
|
||||
await notifications.push(notification, uidsToNotify);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -54,6 +54,15 @@ module.exports = function (Messaging) {
|
||||
data.groupChat = parseInt(data.groupChat, 10) === 1;
|
||||
}
|
||||
|
||||
if (!fields.length || fields.includes('notificationSetting')) {
|
||||
data.notificationSetting = data.notificationSetting ||
|
||||
(
|
||||
data.public ?
|
||||
Messaging.notificationSettings.ATMENTION :
|
||||
Messaging.notificationSettings.ALLMESSAGES
|
||||
);
|
||||
}
|
||||
|
||||
if (data.hasOwnProperty('groups') || !fields.length || fields.includes('groups')) {
|
||||
try {
|
||||
data.groups = JSON.parse(data.groups || '[]');
|
||||
@@ -76,6 +85,7 @@ module.exports = function (Messaging) {
|
||||
const room = {
|
||||
roomId: roomId,
|
||||
timestamp: now,
|
||||
notificationSetting: data.notificationSetting,
|
||||
};
|
||||
|
||||
if (data.hasOwnProperty('roomName') && data.roomName) {
|
||||
@@ -145,10 +155,14 @@ module.exports = function (Messaging) {
|
||||
...roomIds.map(id => `chat:room:${id}:uids`),
|
||||
...roomIds.map(id => `chat:room:${id}:owners`),
|
||||
...roomIds.map(id => `chat:room:${id}:uids:online`),
|
||||
...roomIds.map(id => `chat:room:${id}:notification:settings`),
|
||||
]),
|
||||
db.sortedSetRemove('chat:rooms', roomIds),
|
||||
db.sortedSetRemove('chat:rooms:public', roomIds),
|
||||
db.sortedSetRemove('chat:rooms:public:order', roomIds),
|
||||
db.sortedSetRemove([
|
||||
'chat:rooms',
|
||||
'chat:rooms:public',
|
||||
'chat:rooms:public:order',
|
||||
'chat:rooms:public:lastpost',
|
||||
], roomIds),
|
||||
]);
|
||||
cache.del([
|
||||
'chat:rooms:public:all',
|
||||
@@ -448,7 +462,36 @@ module.exports = function (Messaging) {
|
||||
await db.sortedSetAdd(`chat:room:${roomId}:uids:online`, Date.now(), uid);
|
||||
}
|
||||
|
||||
const [canReply, users, messages, settings, isOwner, onlineUids] = await Promise.all([
|
||||
async function getNotificationOptions() {
|
||||
const userSetting = await db.getObjectField(`chat:room:${roomId}:notification:settings`, uid);
|
||||
const roomDefault = room.notificationSetting;
|
||||
const currentSetting = userSetting || roomDefault;
|
||||
const labels = {
|
||||
[Messaging.notificationSettings.NONE]: { label: '[[modules:chat.notification-setting-none]]', icon: 'fa-ban' },
|
||||
[Messaging.notificationSettings.ATMENTION]: { label: '[[modules:chat.notification-setting-at-mention-only]]', icon: 'fa-at' },
|
||||
[Messaging.notificationSettings.ALLMESSAGES]: { label: '[[modules:chat.notification-setting-all-messages]]', icon: 'fa-comment-o' },
|
||||
};
|
||||
const options = [
|
||||
{
|
||||
label: '[[modules:chat.notification-setting-room-default]]',
|
||||
subLabel: labels[roomDefault].label || '',
|
||||
icon: labels[roomDefault].icon,
|
||||
value: -1,
|
||||
selected: userSetting === null,
|
||||
},
|
||||
];
|
||||
Object.keys(labels).forEach((key) => {
|
||||
options.push({
|
||||
label: labels[key].label,
|
||||
icon: labels[key].icon,
|
||||
value: key,
|
||||
selected: parseInt(userSetting, 10) === parseInt(key, 10),
|
||||
});
|
||||
});
|
||||
return { options, selectedIcon: labels[currentSetting].icon };
|
||||
}
|
||||
|
||||
const [canReply, users, messages, settings, isOwner, onlineUids, notifOptions] = await Promise.all([
|
||||
Messaging.canReply(roomId, uid),
|
||||
Messaging.getUsersInRoomFromSet(`chat:room:${roomId}:uids:online`, roomId, 0, 39, true),
|
||||
Messaging.getMessages({
|
||||
@@ -460,6 +503,7 @@ module.exports = function (Messaging) {
|
||||
user.getSettings(uid),
|
||||
Messaging.isRoomOwner(uid, roomId),
|
||||
io.getUidsInRoom(`chat_room_${roomId}`),
|
||||
getNotificationOptions(),
|
||||
]);
|
||||
|
||||
users.forEach((user) => {
|
||||
@@ -481,6 +525,8 @@ module.exports = function (Messaging) {
|
||||
room.showUserInput = !room.maximumUsersInChatRoom || room.maximumUsersInChatRoom > 2;
|
||||
room.isAdminOrGlobalMod = isAdmin || isGlobalMod;
|
||||
room.isAdmin = isAdmin;
|
||||
room.notificationOptions = notifOptions.options;
|
||||
room.notificationOptionsIcon = notifOptions.selectedIcon;
|
||||
|
||||
const payload = await plugins.hooks.fire('filter:messaging.loadRoom', { uid, data, room });
|
||||
return payload.room;
|
||||
|
||||
@@ -33,6 +33,25 @@ module.exports = function (Messaging) {
|
||||
};
|
||||
|
||||
Messaging.hasRead = async (uids, roomId) => {
|
||||
if (!uids.length) {
|
||||
return [];
|
||||
}
|
||||
const roomData = await Messaging.getRoomData(roomId);
|
||||
if (!roomData) {
|
||||
return uids.map(() => false);
|
||||
}
|
||||
if (roomData.public) {
|
||||
const [userTimestamps, mids] = await Promise.all([
|
||||
db.getObjectsFields(uids.map(uid => `uid:${uid}:chat:rooms:read`), [roomId]),
|
||||
db.getSortedSetRevRangeWithScores(`chat:room:${roomId}:mids`, 0, 0),
|
||||
]);
|
||||
const lastMsgTimestamp = mids[0] ? mids[0].score : 0;
|
||||
return uids.map(
|
||||
(uid, index) => !userTimestamps[index] ||
|
||||
!userTimestamps[index][roomId] ||
|
||||
parseInt(userTimestamps[index][roomId], 10) > lastMsgTimestamp
|
||||
);
|
||||
}
|
||||
const isMembers = await db.isMemberOfSortedSets(
|
||||
uids.map(uid => `uid:${uid}:chat:rooms:unread`),
|
||||
roomId
|
||||
|
||||
@@ -159,7 +159,7 @@ Notifications.push = async function (notification, uids) {
|
||||
winston.error(err.stack);
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
async function pushToUids(uids, notification) {
|
||||
|
||||
@@ -200,4 +200,17 @@ SocketModules.chats.toggleOwner = async (socket, data) => {
|
||||
await Messaging.toggleOwner(data.uid, data.roomId);
|
||||
};
|
||||
|
||||
SocketModules.chats.setNotificationSetting = async (socket, data) => {
|
||||
if (!data || !utils.isNumber(data.value) || !data.roomId) {
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
|
||||
const inRoom = await Messaging.isUserInRoom(socket.uid, data.roomId);
|
||||
if (!inRoom) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
|
||||
await Messaging.setUserNotificationSetting(socket.uid, data.roomId, data.value);
|
||||
};
|
||||
|
||||
require('../promisify')(SocketModules);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<div class="mb-3">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">[[modules:chat.room-name-optional]]</label>
|
||||
<input component="chat/room/name" class="form-control"/>
|
||||
<label class="form-label text-nowrap">[[modules:chat.room-name-optional]]</label>
|
||||
<input component="chat/room/name" class="form-control" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="dropdown mb-3">
|
||||
<div class="dropdown">
|
||||
<label class="form-label">[[modules:chat.add-user]]</label>
|
||||
<input component="chat/search" class="form-control" type="text" placeholder="[[global:user-search-prompt]]" data-bs-toggle="dropdown"/>
|
||||
<ul component="chat/search/list" class="dropdown-menu p-1 overflow-auto" style="max-height: 400px;">
|
||||
@@ -15,7 +16,7 @@
|
||||
{{{ end }}}
|
||||
</ul>
|
||||
</div>
|
||||
<ul component="chat/room/users" class="list-group">
|
||||
<ul component="chat/room/users" class="list-group mt-2">
|
||||
{{{ each selectedUsers }}}
|
||||
<li class="list-group-item d-flex gap-2 align-items-center justify-content-between" component="chat/user" data-uid="{./uid}">
|
||||
<a href="#" class="text-reset text-decoration-none">{buildAvatar(@value, "24px", true)} {./username}</a>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="mb-3">
|
||||
<div class="">
|
||||
<label class="form-label">[[modules:chat.add-user]]</label>
|
||||
<input component="chat/manage/user/add/search" class="form-control" type="text" placeholder="[[global:user-search-prompt]]" />
|
||||
<p class="text-danger"></p>
|
||||
@@ -12,16 +12,29 @@
|
||||
<li class="list-group-item"><i class="fa fa-spinner fa-spin"></i> [[modules:chat.retrieving-users]]</li>
|
||||
</ul>
|
||||
|
||||
{{{ if (user.isAdmin && group.public ) }}}
|
||||
{{{ if user.isAdmin }}}
|
||||
<hr/>
|
||||
<div class="d-flex gap-2 mb-3 align-items-center justify-content-between">
|
||||
<label class="form-label text-nowrap mb-0">[[modules:chat.default-notification-setting]]</label>
|
||||
<select component="chat/room/notification/setting" class="form-select" style="width: 200px;">
|
||||
<option value="1" {{{ if (room.notificationSetting == "1") }}}selected{{{ end }}}>[[modules:chat.notification-setting-none]]</option>
|
||||
<option value="2" {{{ if (room.notificationSetting == "2") }}}selected{{{ end }}}>[[modules:chat.notification-setting-at-mention-only]]</option>
|
||||
<option value="3" {{{ if (room.notificationSetting == "3") }}}selected{{{ end }}}>[[modules:chat.notification-setting-all-messages]]</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{{ if room.public }}}
|
||||
|
||||
<label class="form-label">[[modules:chat.select-groups]]</label>
|
||||
|
||||
<select component="chat/room/groups" class="form-select mb-1" multiple size="10">
|
||||
<select component="chat/room/groups" class="form-select mb-3" multiple size="10">
|
||||
{{{ each groups }}}
|
||||
<option value="{./displayName}" {{{ if ./selected }}}selected{{{ end }}}>{./displayName}</option>
|
||||
{{{ end }}}
|
||||
</select>
|
||||
{{{ end }}}
|
||||
<div class="d-flex justify-content-end">
|
||||
<button component="chat/manage/save/groups" class="btn btn-sm btn-primary">[[global:save]]</button>
|
||||
<button component="chat/manage/save" class="btn btn-sm btn-primary">[[global:save]]</button>
|
||||
</div>
|
||||
{{{ end }}}
|
||||
</div>
|
||||
Reference in New Issue
Block a user