diff --git a/app/discussion/client/views/creationDialog/CreateDiscussion.html b/app/discussion/client/views/creationDialog/CreateDiscussion.html index 847cbb6e9944..837be516fde8 100644 --- a/app/discussion/client/views/creationDialog/CreateDiscussion.html +++ b/app/discussion/client/views/creationDialog/CreateDiscussion.html @@ -96,13 +96,11 @@ - {{#with config}} {{#if autocomplete 'isShowing'}} - - {{/if}} {{/with}} + {{/with}}
{{ description }}
diff --git a/app/emoji/client/emojiParser.js b/app/emoji/client/emojiParser.js index ff1a7b3dee1b..809b3c41569a 100644 --- a/app/emoji/client/emojiParser.js +++ b/app/emoji/client/emojiParser.js @@ -1,61 +1,62 @@ +import s from 'underscore.string'; + import { Meteor } from 'meteor/meteor'; +import { Tracker } from 'meteor/tracker'; + import { getUserPreference } from '../../utils'; import { callbacks } from '../../callbacks'; import { emoji } from '../lib/rocketchat'; -import { isSetNotNull } from './function-isSet'; -import s from 'underscore.string'; /* * emojiParser is a function that will replace emojis * @param {Object} message - The message object */ -callbacks.add('renderMessage', (message) => { - if (isSetNotNull(() => getUserPreference(Meteor.userId(), 'useEmojis')) && - !getUserPreference(Meteor.userId(), 'useEmojis')) { - return message; + +Tracker.autorun(() => { + if (!getUserPreference(Meteor.userId(), 'useEmojis')) { + return callbacks.remove('renderMessage', 'emoji'); } + callbacks.add('renderMessage', (message) => { + if (s.trim(message.html)) { + // ' to apostrophe (') for emojis such as :') + message.html = message.html.replace(/'/g, '\''); + + // '
' to '
' for emojis such at line breaks + message.html = message.html.replace(/
/g, '
'); - if (s.trim(message.html)) { - // ' to apostrophe (') for emojis such as :') - message.html = message.html.replace(/'/g, '\''); + message.html = Object.entries(emoji.packages).reduce((value, [, emojiPackage]) => emojiPackage.render(value), message.html); - // '
' to '
' for emojis such at line breaks - message.html = message.html.replace(/
/g, '
'); + const checkEmojiOnly = $(`
${ message.html }
`); + let emojiOnly = true; - Object.keys(emoji.packages).forEach((emojiPackage) => { - message.html = emoji.packages[emojiPackage].render(message.html); - }); - const checkEmojiOnly = $(`
${ message.html }
`); - let emojiOnly = true; - for (const childNode in checkEmojiOnly[0].childNodes) { - if (checkEmojiOnly[0].childNodes.hasOwnProperty(childNode)) { - const child = $(checkEmojiOnly[0].childNodes[childNode]); + for (let i = 0, len = checkEmojiOnly[0].childNodes.length; i < len; i++) { + const childNode = checkEmojiOnly[0].childNodes[i]; - if (child.hasClass('emoji') || child.hasClass('emojione')) { - checkEmojiOnly[0].childNodes[childNode] = child.addClass('big'); + if (childNode.classList && (childNode.classList.contains('emoji') || childNode.classList.contains('emojione'))) { + childNode.classList.add('big'); continue; } - if (s.trim(child.text()) === '') { + if (s.trim(childNode.innerText) === '') { continue; } emojiOnly = false; break; } - } - if (emojiOnly) { - message.html = checkEmojiOnly.unwrap().html(); - } + if (emojiOnly) { + message.html = checkEmojiOnly.unwrap().html(); + } - // apostrophe (') back to ' - message.html = message.html.replace(/\'/g, '''); + // apostrophe (') back to ' + message.html = message.html.replace(/\'/g, '''); - // apostrophe '
' back to '
' - message.html = message.html.replace(/
/g, '
'); - } + // apostrophe '
' back to '
' + message.html = message.html.replace(/
/g, '
'); + } - return message; -}, callbacks.priority.LOW, 'emoji'); + return message; + }, callbacks.priority.LOW, 'emoji'); +}); diff --git a/app/highlight-words/client/client.js b/app/highlight-words/client/client.js index a740542bf242..b2e811b3b870 100644 --- a/app/highlight-words/client/client.js +++ b/app/highlight-words/client/client.js @@ -2,28 +2,39 @@ * Highlights is a named function that will process Highlights * @param {Object} message - The message object */ +import _ from 'underscore'; +import s from 'underscore.string'; + import { Meteor } from 'meteor/meteor'; +import { Tracker } from 'meteor/tracker'; + import { callbacks } from '../../callbacks'; import { getUserPreference } from '../../utils'; -import _ from 'underscore'; -import s from 'underscore.string'; -import { highlightWords } from './helper'; +import { highlightWords, getRegexHighlight, getRegexHighlightUrl } from './helper'; -function HighlightWordsClient(message) { - let msg = message; - if (!_.isString(message)) { - if (s.trim(message.html)) { - msg = message.html; - } else { - return message; - } +Tracker.autorun(() => { + const toHighlight = (getUserPreference(Meteor.userId(), 'highlights') || []).filter((highlight) => !s.isBlank(highlight)).map((highlight) => ({ + highlight, + regex: getRegexHighlight(highlight), + urlRegex: getRegexHighlightUrl(highlight), + })); + + if (!toHighlight.length) { + return callbacks.remove('renderMessage', 'highlight-words'); } - const to_highlight = getUserPreference(Meteor.user(), 'highlights'); - msg = highlightWords(msg, to_highlight); + function HighlightWordsClient(message) { + let msg = message; - message.html = msg; - return message; -} + if (!_.isString(message)) { + if (!s.trim(message.html)) { + return message; + } + msg = message.html; + } -callbacks.add('renderMessage', HighlightWordsClient, callbacks.priority.MEDIUM + 1, 'highlight-words'); + message.html = highlightWords(msg, toHighlight); + return message; + } + callbacks.add('renderMessage', HighlightWordsClient, callbacks.priority.MEDIUM + 1, 'highlight-words'); +}); diff --git a/app/highlight-words/client/helper.js b/app/highlight-words/client/helper.js index 3f7ef9edd3fa..a82bbdc2a4e0 100644 --- a/app/highlight-words/client/helper.js +++ b/app/highlight-words/client/helper.js @@ -1,42 +1,28 @@ import s from 'underscore.string'; -export const checkHighlightedWordsInUrls = (msg, highlight) => { - const urlRegex = new RegExp(`https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)(${ s.escapeRegExp(highlight) })\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)`, 'gmi'); - const urlMatches = msg.match(urlRegex); - - return urlMatches; -}; +export const checkHighlightedWordsInUrls = (msg, urlRegex) => msg.match(urlRegex); export const removeHighlightedUrls = (msg, highlight, urlMatches) => { + const highlightRegex = new RegExp(highlight, 'gmi'); - urlMatches.forEach((match) => { - const highlightRegex = new RegExp(highlight, 'gmi'); + return urlMatches.reduce((msg, match) => { const withTemplate = match.replace(highlightRegex, `${ highlight }`); const regexWithTemplate = new RegExp(withTemplate, 'i'); - - msg = msg.replace(regexWithTemplate, match); - }); - - return msg; + return msg.replace(regexWithTemplate, match); + }, msg); }; -export const highlightWords = (msg, to_highlight) => { - const highlightTemplate = '$1$2$3'; +const highlightTemplate = '$1$2$3'; - if (Array.isArray(to_highlight)) { - to_highlight.forEach((highlight) => { - if (!s.isBlank(highlight)) { - const regex = new RegExp(`(^|\\b|[\\s\\n\\r\\t.,،'\\\"\\+!?:-])(${ s.escapeRegExp(highlight) })($|\\b|[\\s\\n\\r\\t.,،'\\\"\\+!?:-])(?![^<]*>|[^<>]*<\\/)`, 'gmi'); - const urlMatches = checkHighlightedWordsInUrls(msg, highlight); +export const getRegexHighlight = (highlight) => new RegExp(`(^|\\b|[\\s\\n\\r\\t.,،'\\\"\\+!?:-])(${ s.escapeRegExp(highlight) })($|\\b|[\\s\\n\\r\\t.,،'\\\"\\+!?:-])(?![^<]*>|[^<>]*<\\/)`, 'gmi'); - msg = msg.replace(regex, highlightTemplate); +export const getRegexHighlightUrl = (highlight) => new RegExp(`https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)(${ s.escapeRegExp(highlight) })\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)`, 'gmi'); - if (urlMatches) { - msg = removeHighlightedUrls(msg, highlight, urlMatches); - } - } - }); - } +export const highlightWords = (msg, highlights) => highlights.reduce((msg, { highlight, regex, urlRegex }) => { + const urlMatches = checkHighlightedWordsInUrls(msg, urlRegex); + if (!urlMatches) { + return msg.replace(regex, highlightTemplate); - return msg; -}; + } + return removeHighlightedUrls(msg.replace(regex, highlightTemplate), highlight, urlMatches); +}, msg); diff --git a/app/highlight-words/tests/helper.tests.js b/app/highlight-words/tests/helper.tests.js index 033b18dcde03..16e3812138bd 100644 --- a/app/highlight-words/tests/helper.tests.js +++ b/app/highlight-words/tests/helper.tests.js @@ -2,32 +2,48 @@ import 'babel-polyfill'; import assert from 'assert'; -import { highlightWords } from '../client/helper'; +import { highlightWords, getRegexHighlight, getRegexHighlightUrl } from '../client/helper'; describe('helper', () => { describe('highlightWords', () => { it('highlights the correct words', () => { - const res = highlightWords('here is some word', ['word']); + const res = highlightWords('here is some word', ['word'].map((highlight) => ({ + highlight, + regex: getRegexHighlight(highlight), + urlRegex: getRegexHighlightUrl(highlight), + }))); assert.equal(res, 'here is some word'); }); describe('handles links', () => { it('not highlighting one link', () => { - const res = highlightWords('here we go https://somedomain.com/here-some.word/pulls more words after', ['word']); + const res = highlightWords('here we go https://somedomain.com/here-some.word/pulls more words after', ['word'].map((highlight) => ({ + highlight, + regex: getRegexHighlight(highlight), + urlRegex: getRegexHighlightUrl(highlight), + }))); assert.equal(res, 'here we go https://somedomain.com/here-some.word/pulls more words after'); }); it('not highlighting two links', () => { const msg = 'here https://somedomain.com/here-some-foo/pulls more words after http://www.domain.com/some.foo/bar words after'; - const res = highlightWords(msg, ['foo']); + const res = highlightWords(msg, ['foo'].map((highlight) => ({ + highlight, + regex: getRegexHighlight(highlight), + urlRegex: getRegexHighlightUrl(highlight), + }))); assert.equal(res, msg); }); it('not highlighting link but keep words on message highlighted', () => { - const res = highlightWords('here we go https://somedomain.com/here-some.foo/pulls more foo after', ['foo']); + const res = highlightWords('here we go https://somedomain.com/here-some.foo/pulls more foo after', ['foo'].map((highlight) => ({ + highlight, + regex: getRegexHighlight(highlight), + urlRegex: getRegexHighlightUrl(highlight), + }))); assert.equal(res, 'here we go https://somedomain.com/here-some.foo/pulls more foo after'); }); diff --git a/app/lib/client/lib/formatDate.js b/app/lib/client/lib/formatDate.js index 2d2bd13b785a..e1ae40d79357 100644 --- a/app/lib/client/lib/formatDate.js +++ b/app/lib/client/lib/formatDate.js @@ -1,21 +1,34 @@ +import moment from 'moment'; + import { Meteor } from 'meteor/meteor'; +import { Tracker } from 'meteor/tracker'; + import { getUserPreference, t } from '../../../utils'; import { settings } from '../../../settings'; -import moment from 'moment'; + +let lastDay = t('yesterday'); +let clockMode; +let sameDay; +const dayFormat = ['h:mm A', 'H:mm']; + +Meteor.startup(() => Tracker.autorun(() => { + clockMode = getUserPreference(Meteor.userId(), 'clockMode', false); + sameDay = dayFormat[clockMode - 1] || 'h:mm A'; + lastDay = t('yesterday'); +})); export const formatTime = (time) => { - switch (getUserPreference(Meteor.userId(), 'clockMode', false)) { + switch (clockMode) { case 1: - return moment(time).format('h:mm A'); case 2: - return moment(time).format('H:mm'); + return moment(time).format(sameDay); default: return moment(time).format(settings.get('Message_TimeFormat')); } }; export const formatDateAndTime = (time) => { - switch (getUserPreference(Meteor.userId(), 'clockMode', false)) { + switch (clockMode) { case 1: return moment(time).format('MMMM D, Y h:mm A'); case 2: @@ -25,15 +38,16 @@ export const formatDateAndTime = (time) => { } }; -export const timeAgo = (time) => { - const now = new Date(); - const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1); - - return ( - (now.getDate() === time.getDate() && moment(time).format('LT')) || - (yesterday.getDate() === time.getDate() && t('yesterday')) || - moment(time).format('L') - ); +const sameElse = function(now) { + const diff = Math.ceil(this.diff(now, 'years', true)); + return diff < 0 ? 'MMM D YYYY' : 'MMM D'; }; +export const timeAgo = (date) => moment(date).calendar(null, { + lastDay: `[${ lastDay }]`, + sameDay, + lastWeek: 'dddd', + sameElse, +}); + export const formatDate = (time) => moment(time).format(settings.get('Message_DateFormat')); diff --git a/app/logger/client/logger.js b/app/logger/client/logger.js index 4ca8ffce787c..92556c3dc874 100644 --- a/app/logger/client/logger.js +++ b/app/logger/client/logger.js @@ -1,35 +1,70 @@ import { Template } from 'meteor/templating'; import _ from 'underscore'; -Template.log = false; +import { getConfig } from '../../ui-utils/client/config'; -Template.logMatch = /.*/; +Template.log = !!(getConfig('debug') || getConfig('debug-template')); + +if (Template.log) { -Template.enableLogs = function(log) { Template.logMatch = /.*/; - if (log === false) { - return Template.log = false; - } else { - Template.log = true; - if (log instanceof RegExp) { - return Template.logMatch = log; + + Template.enableLogs = function(log) { + Template.logMatch = /.*/; + if (log === false) { + return Template.log = false; + } else { + Template.log = true; + if (log instanceof RegExp) { + return Template.logMatch = log; + } } - } -}; + }; -const wrapHelpersAndEvents = function(original, prefix, color) { - return function(dict) { + const wrapHelpersAndEvents = function(original, prefix, color) { + return function(dict) { - const template = this; - const fn1 = function(name, fn) { - if (fn instanceof Function) { - return dict[name] = function(...args) { + const template = this; + const fn1 = function(name, fn) { + if (fn instanceof Function) { + return dict[name] = function(...args) { + + const result = fn.apply(this, args); + if (Template.log === true) { + const completeName = `${ prefix }:${ template.viewName.replace('Template.', '') }.${ name }`; + if (Template.logMatch.test(completeName)) { + console.log(`%c${ completeName }`, `color: ${ color }`, { + args, + scope: this, + result, + }); + } + } + return result; + }; + } + }; + _.each(name, (fn, name) => { + fn1(name, fn); + }); + return original.call(template, dict); + }; + }; + Template.prototype.helpers = wrapHelpersAndEvents(Template.prototype.helpers, 'helper', 'blue'); + + Template.prototype.events = wrapHelpersAndEvents(Template.prototype.events, 'event', 'green'); + + const wrapLifeCycle = function(original, prefix, color) { + return function(fn) { + const template = this; + if (fn instanceof Function) { + const wrap = function(...args) { const result = fn.apply(this, args); if (Template.log === true) { const completeName = `${ prefix }:${ template.viewName.replace('Template.', '') }.${ name }`; if (Template.logMatch.test(completeName)) { - console.log(`%c${ completeName }`, `color: ${ color }`, { + console.log(`%c${ completeName }`, `color: ${ color }; font-weight: bold`, { args, scope: this, result, @@ -38,46 +73,16 @@ const wrapHelpersAndEvents = function(original, prefix, color) { } return result; }; + return original.call(template, wrap); + } else { + return original.call(template, fn); } }; - _.each(name, (fn, name) => { - fn1(name, fn); - }); - return original.call(template, dict); - }; -}; - -Template.prototype.helpers = wrapHelpersAndEvents(Template.prototype.helpers, 'helper', 'blue'); - -Template.prototype.events = wrapHelpersAndEvents(Template.prototype.events, 'event', 'green'); - -const wrapLifeCycle = function(original, prefix, color) { - return function(fn) { - const template = this; - if (fn instanceof Function) { - const wrap = function(...args) { - const result = fn.apply(this, args); - if (Template.log === true) { - const completeName = `${ prefix }:${ template.viewName.replace('Template.', '') }.${ name }`; - if (Template.logMatch.test(completeName)) { - console.log(`%c${ completeName }`, `color: ${ color }; font-weight: bold`, { - args, - scope: this, - result, - }); - } - } - return result; - }; - return original.call(template, wrap); - } else { - return original.call(template, fn); - } }; -}; -Template.prototype.onCreated = wrapLifeCycle(Template.prototype.onCreated, 'onCreated', 'blue'); + Template.prototype.onCreated = wrapLifeCycle(Template.prototype.onCreated, 'onCreated', 'blue'); -Template.prototype.onRendered = wrapLifeCycle(Template.prototype.onRendered, 'onRendered', 'green'); + Template.prototype.onRendered = wrapLifeCycle(Template.prototype.onRendered, 'onRendered', 'green'); -Template.prototype.onDestroyed = wrapLifeCycle(Template.prototype.onDestroyed, 'onDestroyed', 'red'); + Template.prototype.onDestroyed = wrapLifeCycle(Template.prototype.onDestroyed, 'onDestroyed', 'red'); +} diff --git a/app/mentions/client/client.js b/app/mentions/client/client.js index a227c6256865..082fe9dd138d 100644 --- a/app/mentions/client/client.js +++ b/app/mentions/client/client.js @@ -1,12 +1,24 @@ import { Meteor } from 'meteor/meteor'; +import { Tracker } from 'meteor/tracker'; import { callbacks } from '../../callbacks'; import { settings } from '../../settings'; import { MentionsParser } from '../lib/MentionsParser'; +let me; +let useRealName; +let pattern; + +Meteor.startup(() => Tracker.autorun(() => { + me = Meteor.userId() && Meteor.user().username; + pattern = settings.get('UTF8_Names_Validation'); + useRealName = settings.get('UI_Use_Real_Name'); +})); + + const instance = new MentionsParser({ - pattern: () => settings.get('UTF8_Names_Validation'), - useRealName: () => settings.get('UI_Use_Real_Name'), - me: () => Meteor.userId() && Meteor.user().username, + pattern: () => pattern, + useRealName: () => useRealName, + me: () => me, }); callbacks.add('renderMessage', (message) => instance.parse(message), callbacks.priority.MEDIUM, 'mentions-message'); diff --git a/app/theme/client/imports/forms/popup-list.css b/app/theme/client/imports/forms/popup-list.css index 1abcc9ca6fa0..35349a13d96d 100644 --- a/app/theme/client/imports/forms/popup-list.css +++ b/app/theme/client/imports/forms/popup-list.css @@ -5,8 +5,6 @@ width: 100%; - padding: 0 4px; - &__list { overflow-y: auto; diff --git a/app/threads/client/flextab/thread.js b/app/threads/client/flextab/thread.js index eaec26f4e13a..557ae08280d1 100644 --- a/app/threads/client/flextab/thread.js +++ b/app/threads/client/flextab/thread.js @@ -75,8 +75,6 @@ Template.thread.onRendered(function() { this.chatMessages.initializeWrapper(this.find('.js-scroll-thread')); this.chatMessages.initializeInput(this.find('.js-input-message'), { rid, tmid }); - this.chatMessages.wrapper.scrollTop = this.chatMessages.wrapper.scrollHeight - this.chatMessages.wrapper.clientHeight; - this.sendToBottom = _.throttle(() => { this.chatMessages.wrapper.scrollTop = this.chatMessages.wrapper.scrollHeight; }, 300); @@ -94,7 +92,13 @@ Template.thread.onRendered(function() { const tmid = this.state.get('tmid'); this.threadsObserve && this.threadsObserve.stop(); - this.threadsObserve = Messages.find({ tmid, _updatedAt: { $gt: new Date() } }).observe({ + this.threadsObserve = Messages.find({ tmid, _updatedAt: { $gt: new Date() } }, { + fields: { + collapsed: 0, + threadMsg: 0, + repliesCount: 0, + }, + }).observe({ added: ({ _id, ...message }) => { const { atBottom } = this; this.Threads.upsert({ _id }, message); diff --git a/app/threads/client/flextab/threads.js b/app/threads/client/flextab/threads.js index c42e782f7221..8b4b594887dd 100644 --- a/app/threads/client/flextab/threads.js +++ b/app/threads/client/flextab/threads.js @@ -10,12 +10,14 @@ import { call } from '../../../ui-utils'; import { Messages, Subscriptions } from '../../../models'; import { messageContext } from '../../../ui-utils/client/lib/messageContext'; import { messageArgs } from '../../../ui-utils/client/lib/messageArgs'; +import { getConfig } from '../../../ui-utils/client/config'; import { upsert } from '../upsert'; import './threads.html'; -const LIST_SIZE = 50; +const LIST_SIZE = parseInt(getConfig('threadsListSize')) || 50; + const sort = { tlm: -1 }; Template.threads.events({ diff --git a/app/ui-message/client/message.html b/app/ui-message/client/message.html index a08666cff188..e78b42a76034 100644 --- a/app/ui-message/client/message.html +++ b/app/ui-message/client/message.html @@ -101,7 +101,7 @@ {{_ "No_messages_yet" }} {{/if}} - {{ formatDate msg.dlm }} + {{ formatDateAndTime msg.dlm }} {{/if}} @@ -111,7 +111,7 @@ {{> icon icon="thread"}} {{msg.tcount}} {{_ i18nKeyReply}} - {{ formatDate msg.tlm}} + {{ formatDateAndTime msg.tlm}} {{/if}} diff --git a/app/ui-message/client/message.js b/app/ui-message/client/message.js index 738d0fdc47b1..5f3b571a696f 100644 --- a/app/ui-message/client/message.js +++ b/app/ui-message/client/message.js @@ -1,12 +1,11 @@ import _ from 'underscore'; -import moment from 'moment'; import { Meteor } from 'meteor/meteor'; import { Blaze } from 'meteor/blaze'; import { Template } from 'meteor/templating'; import { TAPi18n } from 'meteor/tap:i18n'; -import { timeAgo } from '../../lib/client/lib/formatDate'; +import { timeAgo, formatDateAndTime } from '../../lib/client/lib/formatDate'; import { DateFormat } from '../../lib/client'; import { renderMessageBody, MessageTypes, MessageAction, call, normalizeThreadMessage } from '../../ui-utils/client'; import { RoomRoles, UserRoles, Roles, Messages } from '../../models/client'; @@ -84,9 +83,7 @@ Template.message.helpers({ ? 'replies' : 'reply'; }, - formatDate(date) { - return moment(date).format('LLL'); - }, + formatDateAndTime, encodeURI(text) { return encodeURI(text); }, @@ -519,10 +516,6 @@ const setNewDayAndGroup = (currentNode, previousNode, forceDate, period, noDate) classList.add('new-day'); } - if (previousDataset.tmid !== currentDataset.tmid) { - return classList.remove('sequential'); - } - if (previousDataset.username !== currentDataset.username || parseInt(currentDataset.timestamp) - parseInt(previousDataset.timestamp) > period) { return classList.remove('sequential'); } @@ -559,7 +552,7 @@ Template.message.onViewRendered = function() { nextNode.classList.remove('new-day'); } if (nextDataset.groupable !== 'false') { - if (nextDataset.tmid !== currentDataset.tmid || nextDataset.username !== currentDataset.username || parseInt(nextDataset.timestamp) - parseInt(currentDataset.timestamp) > settings.Message_GroupingPeriod) { + if (nextDataset.username !== currentDataset.username || parseInt(nextDataset.timestamp) - parseInt(currentDataset.timestamp) > settings.Message_GroupingPeriod) { nextNode.classList.remove('sequential'); } else if (!nextNode.classList.contains('new-day') && !currentNode.classList.contains('temp') && !currentNode.dataset.tmid) { nextNode.classList.add('sequential'); diff --git a/app/ui-utils/client/config.js b/app/ui-utils/client/config.js new file mode 100644 index 000000000000..195b1b7db8e0 --- /dev/null +++ b/app/ui-utils/client/config.js @@ -0,0 +1,6 @@ +const url = new URL(window.location); +const keys = new Set(); +export const getConfig = (key) => { + keys.add(key); + return url.searchParams.get(key) || localStorage.getItem(`rc-config-${ key }`); +}; diff --git a/app/ui-utils/client/lib/RoomHistoryManager.js b/app/ui-utils/client/lib/RoomHistoryManager.js index 33c6de31722f..93ed3e774576 100644 --- a/app/ui-utils/client/lib/RoomHistoryManager.js +++ b/app/ui-utils/client/lib/RoomHistoryManager.js @@ -4,6 +4,7 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; import { Blaze } from 'meteor/blaze'; import { ChatMessage, ChatSubscription, ChatRoom } from '../../../models'; +import { getConfig } from '../config'; import { RoomManager } from './RoomManager'; import { readMessage } from './readMessages'; import { renderMessageBody } from './renderMessageBody'; @@ -66,7 +67,7 @@ function upsertMessageBulk({ msgs, subscription }) { }); } -const defaultLimit = parseInt(localStorage && localStorage.getItem('rc-defaultLimit')) || 50 ; +const defaultLimit = parseInt(getConfig('roomListLimit')) || 50 ; export const RoomHistoryManager = new class { constructor() { diff --git a/app/ui-utils/client/lib/RoomManager.js b/app/ui-utils/client/lib/RoomManager.js index 4c02125d58ff..9dd8631cbf5e 100644 --- a/app/ui-utils/client/lib/RoomManager.js +++ b/app/ui-utils/client/lib/RoomManager.js @@ -14,10 +14,10 @@ import { CachedCollectionManager } from '../../../ui-cached-collection'; import _ from 'underscore'; import { upsertMessage, RoomHistoryManager } from './RoomHistoryManager'; import { mainReady } from './mainReady'; +import { getConfig } from '../config'; - -const maxRoomsOpen = parseInt(localStorage && localStorage.getItem('rc-maxRoomsOpen')) || 5 ; +const maxRoomsOpen = parseInt(getConfig('maxRoomsOpen')) || 5 ; const onDeleteMessageStream = (msg) => { ChatMessage.remove({ _id: msg._id }); diff --git a/app/ui/client/components/popupList.html b/app/ui/client/components/popupList.html index a24033e30c07..49318e3aea9e 100644 --- a/app/ui/client/components/popupList.html +++ b/app/ui/client/components/popupList.html @@ -6,16 +6,20 @@ + +