diff --git a/.storybook/.babelrc b/.storybook/.babelrc deleted file mode 100644 index 97ab12b7f95..00000000000 --- a/.storybook/.babelrc +++ /dev/null @@ -1,19 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "shippedProposals": true, - "useBuiltIns": "usage", - "corejs": "3", - "modules": "commonjs" - } - ], - "@babel/preset-react", - "@babel/preset-flow" - ], - "plugins": [ - "@babel/plugin-proposal-class-properties", - "@babel/plugin-proposal-optional-chaining" - ] -} diff --git a/.storybook/addons.js b/.storybook/addons.js deleted file mode 100644 index 9d64a3d0a8f..00000000000 --- a/.storybook/addons.js +++ /dev/null @@ -1,4 +0,0 @@ -import '@storybook/addon-actions/register'; -import '@storybook/addon-knobs/register'; -import '@storybook/addon-links/register'; -import '@storybook/addon-viewport/register'; diff --git a/.storybook/babel.config.js b/.storybook/babel.config.js new file mode 100644 index 00000000000..f028aa2d294 --- /dev/null +++ b/.storybook/babel.config.js @@ -0,0 +1,20 @@ +module.exports = { + presets: [ + [ + '@babel/preset-env', + { + shippedProposals: true, + useBuiltIns: 'usage', + corejs: '3', + modules: 'commonjs', + }, + ], + '@babel/preset-react', + '@babel/preset-flow', + ], + plugins: [ + '@babel/plugin-proposal-class-properties', + '@babel/plugin-proposal-optional-chaining', + '@babel/plugin-proposal-nullish-coalescing-operator', + ], +}; diff --git a/.storybook/config.js b/.storybook/config.js deleted file mode 100644 index c7fb95b2124..00000000000 --- a/.storybook/config.js +++ /dev/null @@ -1,20 +0,0 @@ -import { withKnobs } from '@storybook/addon-knobs'; -import { MINIMAL_VIEWPORTS } from '@storybook/addon-viewport/dist/defaults'; -import { addDecorator, addParameters, configure } from '@storybook/react'; - -import { rocketChatDecorator } from './mocks/decorators'; - -addParameters({ - viewport: { - viewports: MINIMAL_VIEWPORTS, - }, -}); - -addDecorator(rocketChatDecorator); -addDecorator(withKnobs); - -configure([ - require.context('../app', true, /\.stories\.js$/), - require.context('../client', true, /\.stories\.js$/), - require.context('../ee/app', true, /\.stories\.js$/), -], module); diff --git a/.storybook/main.js b/.storybook/main.js new file mode 100644 index 00000000000..2b53b2635b3 --- /dev/null +++ b/.storybook/main.js @@ -0,0 +1,11 @@ +module.exports = { + stories: [ + '../app/**/*.stories.js', + '../client/**/*.stories.js', + '../ee/app/**/*.stories.js', + ], + addons: [ + '@storybook/addon-actions', + '@storybook/addon-knobs', + ], +}; diff --git a/.storybook/mocks/meteor.js b/.storybook/mocks/meteor.js index d85c752a2b2..a690a4db684 100644 --- a/.storybook/mocks/meteor.js +++ b/.storybook/mocks/meteor.js @@ -4,7 +4,10 @@ export const Meteor = { _localStorage: window.localStorage, absoluteUrl: () => {}, userId: () => {}, - Streamer: () => {}, + Streamer: () => ({ + on: () => {}, + removeListener: () => {}, + }), startup: () => {}, methods: () => {}, call: () => {}, @@ -31,10 +34,13 @@ export const Mongo = { }), }; -export const ReactiveVar = () => ({ - get: () => {}, - set: () => {}, -}); +export const ReactiveVar = (val) => { + let currentVal = val; + return { + get: () => currentVal, + set: (val) => { currentVal = val; }, + }; +}; export const ReactiveDict = () => ({ get: () => {}, diff --git a/.storybook/preview.js b/.storybook/preview.js new file mode 100644 index 00000000000..c4b6fca4894 --- /dev/null +++ b/.storybook/preview.js @@ -0,0 +1,7 @@ +import { withKnobs } from '@storybook/addon-knobs'; +import { addDecorator } from '@storybook/react'; + +import { rocketChatDecorator } from './mocks/decorators'; + +addDecorator(rocketChatDecorator); +addDecorator(withKnobs); diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js index 03a6a91feed..1d47404d267 100644 --- a/.storybook/webpack.config.js +++ b/.storybook/webpack.config.js @@ -31,15 +31,35 @@ module.exports = async ({ config }) => { use: '@settlin/spacebars-loader', }); - config.plugins.push(new webpack.NormalModuleReplacementPlugin( - /^meteor/, - require.resolve('./mocks/meteor.js'), - )); - - config.plugins.push(new webpack.NormalModuleReplacementPlugin( - /\.\/server\/index.js/, - require.resolve('./mocks/empty.js'), - )); + config.module.rules.push({ + test: /\.(ts|tsx)$/, + use: [ + { + loader: 'ts-loader', + options: { + compilerOptions: { + noEmit: false, + }, + }, + }, + { + loader: 'react-docgen-typescript-loader', + }, + ], + }); + + config.resolve.extensions.push('.ts', '.tsx'); + + config.plugins.push( + new webpack.NormalModuleReplacementPlugin( + /^meteor/, + require.resolve('./mocks/meteor.js'), + ), + new webpack.NormalModuleReplacementPlugin( + /\/server(\/index.js)$/, + require.resolve('./mocks/empty.js'), + ), + ); config.mode = 'development'; config.optimization.usedExports = true; diff --git a/app/api/server/lib/rooms.js b/app/api/server/lib/rooms.js index 94eb63685f3..b9e73acbfa6 100644 --- a/app/api/server/lib/rooms.js +++ b/app/api/server/lib/rooms.js @@ -56,6 +56,34 @@ export async function findAdminRooms({ uid, filter, types = [], pagination: { of }; } +export async function findAdminRoom({ uid, rid }) { + if (!await hasPermissionAsync(uid, 'view-room-administration')) { + throw new Error('error-not-authorized'); + } + const fields = { + prid: 1, + fname: 1, + name: 1, + t: 1, + cl: 1, + u: 1, + usernames: 1, + usersCount: 1, + muted: 1, + unmuted: 1, + ro: 1, + default: 1, + favorite: 1, + featured: 1, + topic: 1, + msgs: 1, + archived: 1, + tokenpass: 1, + }; + + return Rooms.findOneById(rid, { fields }); +} + export async function findChannelAndPrivateAutocomplete({ uid, selector }) { if (!await hasPermissionAsync(uid, 'view-other-user-channels')) { return { items: [] }; diff --git a/app/api/server/v1/rooms.js b/app/api/server/v1/rooms.js index 2f8a7df23e0..86711691782 100644 --- a/app/api/server/v1/rooms.js +++ b/app/api/server/v1/rooms.js @@ -4,7 +4,7 @@ import Busboy from 'busboy'; import { FileUpload } from '../../../file-upload'; import { Rooms, Messages } from '../../../models'; import { API } from '../api'; -import { findAdminRooms, findChannelAndPrivateAutocomplete } from '../lib/rooms'; +import { findAdminRooms, findChannelAndPrivateAutocomplete, findAdminRoom } from '../lib/rooms'; function findRoomByIdOrName({ params, checkedArchived = true }) { if ((!params.roomId || !params.roomId.trim()) && (!params.roomName || !params.roomName.trim())) { @@ -299,6 +299,22 @@ API.v1.addRoute('rooms.adminRooms', { authRequired: true }, { }, }); +API.v1.addRoute('rooms.adminRooms.getRoom', { authRequired: true }, { + get() { + const { rid } = this.requestParams(); + const room = Promise.await(findAdminRoom({ + uid: this.userId, + rid, + })); + + if (!room) { + return API.v1.failure('not-allowed', 'Not Allowed'); + } + return API.v1.success(room); + }, +}); + + API.v1.addRoute('rooms.autocomplete.channelAndPrivate', { authRequired: true }, { get() { const { selector } = this.queryParams; @@ -312,3 +328,28 @@ API.v1.addRoute('rooms.autocomplete.channelAndPrivate', { authRequired: true }, }))); }, }); + +API.v1.addRoute('rooms.saveRoomSettings', { authRequired: true }, { + post() { + const { rid, ...params } = this.bodyParams; + + const result = Meteor.runAsUser(this.userId, () => Meteor.call('saveRoomSettings', rid, params)); + + return API.v1.success({ rid: result.rid }); + }, +}); + +API.v1.addRoute('rooms.changeArchivationState', { authRequired: true }, { + post() { + const { rid, action } = this.bodyParams; + + let result; + if (action === 'archive') { + result = Meteor.runAsUser(this.userId, () => Meteor.call('archiveRoom', rid)); + } else { + result = Meteor.runAsUser(this.userId, () => Meteor.call('unarchiveRoom', rid)); + } + + return API.v1.success({ result }); + }, +}); diff --git a/app/api/server/v1/users.js b/app/api/server/v1/users.js index 0598d8a430a..5a8be4493a0 100644 --- a/app/api/server/v1/users.js +++ b/app/api/server/v1/users.js @@ -30,6 +30,8 @@ API.v1.addRoute('users.create', { authRequired: true }, { password: String, username: String, active: Match.Maybe(Boolean), + bio: Match.Maybe(String), + statusText: Match.Maybe(String), roles: Match.Maybe(Array), joinDefaultChannels: Match.Maybe(Boolean), requirePasswordChange: Match.Maybe(Boolean), @@ -431,6 +433,7 @@ API.v1.addRoute('users.update', { authRequired: true, twoFactorRequired: true }, name: Match.Maybe(String), password: Match.Maybe(String), username: Match.Maybe(String), + bio: Match.Maybe(String), statusText: Match.Maybe(String), active: Match.Maybe(Boolean), roles: Match.Maybe(Array), diff --git a/app/apps/client/orchestrator.js b/app/apps/client/orchestrator.js index 54c614044fe..0b7bdfd5883 100644 --- a/app/apps/client/orchestrator.js +++ b/app/apps/client/orchestrator.js @@ -4,7 +4,7 @@ import toastr from 'toastr'; import { AppWebsocketReceiver } from './communication'; import { APIClient } from '../../utils'; -import { registerAdminSidebarItem } from '../../ui-admin/client'; +import { registerAdminSidebarItem } from '../../../client/admin'; import { CachedCollectionManager } from '../../ui-cached-collection'; import { hasAtLeastOnePermission } from '../../authorization'; import { handleI18nResources } from './i18n'; diff --git a/app/apps/client/routes.js b/app/apps/client/routes.js index 66005d26b87..c8ff516040e 100644 --- a/app/apps/client/routes.js +++ b/app/apps/client/routes.js @@ -1,7 +1,7 @@ import { FlowRouter } from 'meteor/kadira:flow-router'; import { BlazeLayout } from 'meteor/kadira:blaze-layout'; -import { registerAdminRoute } from '../../ui-admin/client'; +import { registerAdminRoute } from '../../../client/admin'; import { Apps } from './orchestrator'; registerAdminRoute('/apps/what-is-it', { diff --git a/app/authorization/client/route.js b/app/authorization/client/route.js index 95ccd464cc4..2a09c78da6d 100644 --- a/app/authorization/client/route.js +++ b/app/authorization/client/route.js @@ -1,6 +1,6 @@ import { BlazeLayout } from 'meteor/kadira:blaze-layout'; -import { registerAdminRoute } from '../../ui-admin/client'; +import { registerAdminRoute } from '../../../client/admin'; import { t } from '../../utils/client'; registerAdminRoute('/permissions', { diff --git a/app/authorization/client/startup.js b/app/authorization/client/startup.js index 9e437c26c05..2dd50b95fa2 100644 --- a/app/authorization/client/startup.js +++ b/app/authorization/client/startup.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; import { hasAtLeastOnePermission } from './hasPermission'; -import { registerAdminSidebarItem } from '../../ui-admin/client'; +import { registerAdminSidebarItem } from '../../../client/admin'; import { CachedCollectionManager } from '../../ui-cached-collection'; import { APIClient } from '../../utils/client'; import { Roles } from '../../models/client'; diff --git a/app/channel-settings/client/views/Multiselect.js b/app/channel-settings/client/views/Multiselect.js index a63fa1981cd..1f90cc45a38 100644 --- a/app/channel-settings/client/views/Multiselect.js +++ b/app/channel-settings/client/views/Multiselect.js @@ -4,7 +4,7 @@ import { createTemplateForComponent } from '../../../../client/reactAdapters'; createTemplateForComponent( 'Multiselect', - () => import('../../../ui-admin/client/components/settings/inputs/MultiSelectSettingInput'), + () => import('../../../../client/admin/settings/inputs/MultiSelectSettingInput'), { // eslint-disable-next-line new-cap renderContainerView: () => HTML.DIV({ class: 'rc-multiselect', style: 'display: flex;' }), diff --git a/app/chatpal-search/client/route.js b/app/chatpal-search/client/route.js index 86c6ad276c4..29695aa215a 100644 --- a/app/chatpal-search/client/route.js +++ b/app/chatpal-search/client/route.js @@ -1,6 +1,6 @@ import { BlazeLayout } from 'meteor/kadira:blaze-layout'; -import { registerAdminRoute } from '../../ui-admin/client'; +import { registerAdminRoute } from '../../../client/admin'; import { t } from '../../utils'; registerAdminRoute('/chatpal', { diff --git a/app/cloud/client/index.js b/app/cloud/client/index.js index 0c8025d22db..ce6ceecd3d5 100644 --- a/app/cloud/client/index.js +++ b/app/cloud/client/index.js @@ -4,7 +4,7 @@ import './admin/cloudRegisterManually'; import { BlazeLayout } from 'meteor/kadira:blaze-layout'; -import { registerAdminRoute, registerAdminSidebarItem } from '../../ui-admin/client'; +import { registerAdminRoute, registerAdminSidebarItem } from '../../../client/admin'; import { hasAtLeastOnePermission } from '../../authorization'; registerAdminRoute('/cloud', { diff --git a/app/custom-sounds/assets/stylesheets/customSoundsAdmin.css b/app/custom-sounds/assets/stylesheets/customSoundsAdmin.css deleted file mode 100644 index c96075a621e..00000000000 --- a/app/custom-sounds/assets/stylesheets/customSoundsAdmin.css +++ /dev/null @@ -1,111 +0,0 @@ -.sound-info { - & .icon-play-circled { - cursor: pointer; - } -} - -.sound-view { - z-index: 15; - - overflow-x: hidden; - overflow-y: auto; - - & .thumb { - width: 100%; - height: 350px; - padding: 20px; - } - - & nav { - padding: 0 20px; - } - - & .info { - padding: 0 20px; - - white-space: normal; - - & h3 { - overflow: hidden; - - width: 100%; - margin: 8px 0; - - user-select: text; - white-space: nowrap; - text-overflow: ellipsis; - - font-size: 24px; - line-height: 27px; - - & i::after { - display: inline-block; - - width: 8px; - height: 8px; - - content: " "; - vertical-align: middle; - - border-radius: 4px; - } - } - - & p { - -webkit-user-select: text; - -moz-user-select: text; - -ms-user-select: text; - user-select: text; - - font-size: 12px; - font-weight: 300; - line-height: 18px; - } - } - - & .edit-form { - padding: 20px 20px 0; - - white-space: normal; - - & h3 { - margin-bottom: 8px; - - font-size: 24px; - line-height: 22px; - } - - & p { - font-size: 12px; - font-weight: 300; - line-height: 18px; - } - - & > .input-line { - margin-top: 20px; - } - - & nav { - padding: 0; - - &.buttons { - margin-top: 2em; - } - } - - & .form-divisor { - height: 9px; - margin: 2em 0; - - text-align: center; - - & > span { - padding: 0 1em; - } - } - } - - & .room-info-content > div { - margin: 0 0 20px; - } -} diff --git a/app/custom-sounds/client/admin/adminSoundEdit.html b/app/custom-sounds/client/admin/adminSoundEdit.html deleted file mode 100644 index c6f4ef42f9b..00000000000 --- a/app/custom-sounds/client/admin/adminSoundEdit.html +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/app/custom-sounds/client/admin/adminSoundInfo.html b/app/custom-sounds/client/admin/adminSoundInfo.html deleted file mode 100644 index f9676c22303..00000000000 --- a/app/custom-sounds/client/admin/adminSoundInfo.html +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/app/custom-sounds/client/admin/adminSounds.html b/app/custom-sounds/client/admin/adminSounds.html deleted file mode 100644 index c9a20bd9c4f..00000000000 --- a/app/custom-sounds/client/admin/adminSounds.html +++ /dev/null @@ -1,78 +0,0 @@ - diff --git a/app/custom-sounds/client/admin/adminSounds.js b/app/custom-sounds/client/admin/adminSounds.js deleted file mode 100644 index 9de092e3c14..00000000000 --- a/app/custom-sounds/client/admin/adminSounds.js +++ /dev/null @@ -1,176 +0,0 @@ -import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; -import { FlowRouter } from 'meteor/kadira:flow-router'; -import { Template } from 'meteor/templating'; -import _ from 'underscore'; - -import { RocketChatTabBar, SideNav, TabBar } from '../../../ui-utils'; -import { CustomSounds } from '../lib/CustomSounds'; -import { APIClient } from '../../../utils/client'; - -const LIST_SIZE = 50; -const DEBOUNCE_TIME_TO_SEARCH_IN_MS = 500; - -Template.adminSounds.helpers({ - searchText() { - const instance = Template.instance(); - return instance.filter && instance.filter.get(); - }, - isPlaying(_id) { - return Template.instance().isPlayingId.get() === _id; - }, - customsounds() { - return Template.instance().sounds.get(); - }, - isLoading() { - return Template.instance().isLoading.get(); - }, - flexData() { - return { - tabBar: Template.instance().tabBar, - data: Template.instance().tabBarData.get(), - }; - }, - - onTableScroll() { - const instance = Template.instance(); - return function(currentTarget) { - if (currentTarget.offsetHeight + currentTarget.scrollTop < currentTarget.scrollHeight - 100) { - return; - } - const sounds = instance.sounds.get(); - if (instance.total.get() > sounds.length) { - instance.offset.set(instance.offset.get() + LIST_SIZE); - } - }; - }, - onTableItemClick() { - const instance = Template.instance(); - return function(item) { - instance.tabBarData.set({ - sound: instance.sounds.get().find((sound) => sound._id === item._id), - onSuccess: instance.onSuccessCallback, - }); - instance.tabBar.showGroup('custom-sounds-selected'); - instance.tabBar.open('admin-sound-info'); - }; - }, -}); - -Template.adminSounds.onCreated(function() { - const instance = this; - this.sounds = new ReactiveVar([]); - this.offset = new ReactiveVar(0); - this.total = new ReactiveVar(0); - this.query = new ReactiveVar({}); - this.isLoading = new ReactiveVar(false); - this.filter = new ReactiveVar(''); - this.isPlayingId = new ReactiveVar(''); - - this.tabBar = new RocketChatTabBar(); - this.tabBar.showGroup(FlowRouter.current().route.name); - this.tabBarData = new ReactiveVar(); - - TabBar.addButton({ - groups: ['custom-sounds', 'custom-sounds-selected'], - id: 'add-sound', - i18nTitle: 'Custom_Sound_Add', - icon: 'plus', - template: 'adminSoundEdit', - order: 1, - }); - - TabBar.addButton({ - groups: ['custom-sounds-selected'], - id: 'admin-sound-info', - i18nTitle: 'Custom_Sound_Info', - icon: 'customize', - template: 'adminSoundInfo', - order: 2, - }); - - this.onSuccessCallback = () => { - this.offset.set(0); - return this.loadSounds(this.query.get(), this.offset.get()); - }; - - this.tabBarData.set({ - onSuccess: instance.onSuccessCallback, - }); - - this.loadSounds = _.debounce(async (query, offset) => { - this.isLoading.set(true); - const { sounds, total } = await APIClient.v1.get(`custom-sounds.list?count=${ LIST_SIZE }&offset=${ offset }&query=${ JSON.stringify(query) }`); - this.total.set(total); - if (offset === 0) { - this.sounds.set(sounds); - } else { - this.sounds.set(this.sounds.get().concat(sounds)); - } - this.isLoading.set(false); - }, DEBOUNCE_TIME_TO_SEARCH_IN_MS); - - this.autorun(() => { - const filter = this.filter.get() && this.filter.get().trim(); - const offset = this.offset.get(); - if (filter) { - const regex = { $regex: filter, $options: 'i' }; - return this.loadSounds({ name: regex }, offset); - } - return this.loadSounds({}, offset); - }); -}); - -Template.adminSounds.onRendered(() => - Tracker.afterFlush(function() { - SideNav.setFlex('adminFlex'); - SideNav.openFlex(); - }), -); - -Template.adminSounds.events({ - 'keydown #sound-filter'(e) { - // stop enter key - if (e.which === 13) { - e.stopPropagation(); - e.preventDefault(); - } - }, - 'keyup #sound-filter'(e, t) { - e.stopPropagation(); - e.preventDefault(); - t.filter.set(e.currentTarget.value); - t.offset.set(0); - }, - 'click .icon-play-circled'(e, t) { - e.preventDefault(); - e.stopPropagation(); - CustomSounds.play(this._id); - const audio = document.getElementById(t.isPlayingId.get()); - if (audio) { - audio.pause(); - } - document.getElementById(this._id).onended = () => { - t.isPlayingId.set(''); - this.onended = null; - }; - t.isPlayingId.set(this._id); - }, - 'click .icon-pause-circled'(e, t) { - e.preventDefault(); - e.stopPropagation(); - const audio = document.getElementById(this._id); - if (audio && !audio.paused) { - audio.pause(); - } - t.isPlayingId.set(''); - }, - 'click .icon-reset-circled'(e) { - e.preventDefault(); - e.stopPropagation(); - const audio = document.getElementById(this._id); - if (audio) { - audio.currentTime = 0; - } - }, -}); diff --git a/app/custom-sounds/client/admin/route.js b/app/custom-sounds/client/admin/route.js deleted file mode 100644 index 0f181fda578..00000000000 --- a/app/custom-sounds/client/admin/route.js +++ /dev/null @@ -1,11 +0,0 @@ -import { BlazeLayout } from 'meteor/kadira:blaze-layout'; - -import { registerAdminRoute } from '../../../ui-admin/client'; - -registerAdminRoute('/custom-sounds', { - name: 'custom-sounds', - async action(/* params*/) { - await import('./views'); - BlazeLayout.render('main', { center: 'adminSounds' }); - }, -}); diff --git a/app/custom-sounds/client/admin/soundEdit.html b/app/custom-sounds/client/admin/soundEdit.html deleted file mode 100644 index 63caf62d4dd..00000000000 --- a/app/custom-sounds/client/admin/soundEdit.html +++ /dev/null @@ -1,25 +0,0 @@ - diff --git a/app/custom-sounds/client/admin/soundEdit.js b/app/custom-sounds/client/admin/soundEdit.js deleted file mode 100644 index cffe9c92d10..00000000000 --- a/app/custom-sounds/client/admin/soundEdit.js +++ /dev/null @@ -1,155 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Template } from 'meteor/templating'; -import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; -import toastr from 'toastr'; -import s from 'underscore.string'; - -import { t, handleError } from '../../../utils'; - -Template.soundEdit.helpers({ - sound() { - return Template.instance().sound; - }, - - name() { - return this.name || this._id; - }, -}); - -Template.soundEdit.events({ - 'click .cancel'(e, t) { - e.stopPropagation(); - e.preventDefault(); - delete Template.instance().soundFile; - t.cancel(t.find('form')); - }, - - 'submit form'(e, t) { - e.stopPropagation(); - e.preventDefault(); - t.save(e.currentTarget); - }, - - 'change input[type=file]'(ev) { - const e = ev.originalEvent != null ? ev.originalEvent : ev; - let { files } = e.target; - if (e.target.files == null || files.length === 0) { - if (e.dataTransfer.files != null) { - files = e.dataTransfer.files; - } else { - files = []; - } - } - - // using let x of y here seems to have incompatibility with some phones - for (const file in files) { - if (files.hasOwnProperty(file)) { - Template.instance().soundFile = files[file]; - } - } - }, -}); - -Template.soundEdit.onCreated(function() { - if (this.data != null) { - this.sound = this.data.sound; - } else { - this.sound = undefined; - this.data.tabBar.showGroup('custom-sounds'); - } - this.onSuccess = Template.currentData().onSuccess; - this.cancel = (form, name) => { - form.reset(); - this.data.tabBar.close(); - if (this.sound) { - this.data.back(name); - } - }; - - this.getSoundData = () => { - const soundData = {}; - if (this.sound != null) { - soundData._id = this.sound._id; - soundData.previousName = this.sound.name; - soundData.extension = this.sound.extension; - soundData.previousExtension = this.sound.extension; - } - soundData.name = s.trim(this.$('#name').val()); - soundData.newFile = false; - return soundData; - }; - - this.validate = () => { - const soundData = this.getSoundData(); - - const errors = []; - if (!soundData.name) { - errors.push('Name'); - } - - if (!soundData._id) { - if (!this.soundFile) { - errors.push('Sound_File_mp3'); - } - } - - for (const error of errors) { - toastr.error(TAPi18n.__('error-the-field-is-required', { field: TAPi18n.__(error) })); - } - - if (this.soundFile) { - if (!/audio\/mp3/.test(this.soundFile.type) && !/audio\/mpeg/.test(this.soundFile.type) && !/audio\/x-mpeg/.test(this.soundFile.type)) { - errors.push('FileType'); - toastr.error(TAPi18n.__('error-invalid-file-type')); - } - } - - return errors.length === 0; - }; - - this.save = (form) => { - if (this.validate()) { - const soundData = this.getSoundData(); - - if (this.soundFile) { - soundData.newFile = true; - soundData.extension = this.soundFile.name.split('.').pop(); - soundData.type = this.soundFile.type; - } - - Meteor.call('insertOrUpdateSound', soundData, (error, result) => { - if (result) { - soundData._id = result; - soundData.random = Math.round(Math.random() * 1000); - - if (this.soundFile) { - toastr.info(TAPi18n.__('Uploading_file')); - - const reader = new FileReader(); - reader.readAsBinaryString(this.soundFile); - reader.onloadend = () => { - Meteor.call('uploadCustomSound', reader.result, this.soundFile.type, soundData, (uploadError/* , data*/) => { - if (uploadError != null) { - handleError(uploadError); - console.log(uploadError); - } - }, - ); - delete this.soundFile; - toastr.success(TAPi18n.__('File_uploaded')); - }; - } - - toastr.success(t('Custom_Sound_Saved_Successfully')); - this.onSuccess(); - - this.cancel(form, soundData.name); - } - - if (error) { - handleError(error); - } - }); - } - }; -}); diff --git a/app/custom-sounds/client/admin/soundInfo.html b/app/custom-sounds/client/admin/soundInfo.html deleted file mode 100644 index 09374f00609..00000000000 --- a/app/custom-sounds/client/admin/soundInfo.html +++ /dev/null @@ -1,19 +0,0 @@ - diff --git a/app/custom-sounds/client/admin/soundInfo.js b/app/custom-sounds/client/admin/soundInfo.js deleted file mode 100644 index a27c74a03b2..00000000000 --- a/app/custom-sounds/client/admin/soundInfo.js +++ /dev/null @@ -1,118 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Template } from 'meteor/templating'; - -import { modal } from '../../../ui-utils'; -import { t, handleError } from '../../../utils'; - -Template.soundInfo.helpers({ - name() { - const sound = Template.instance().sound.get(); - return sound.name; - }, - - sound() { - return Template.instance().sound.get(); - }, - - editingSound() { - return Template.instance().editingSound.get(); - }, - - soundToEdit() { - const instance = Template.instance(); - return { - tabBar: instance.data.tabBar, - data: instance.data.data, - sound: instance.sound.get(), - onSuccess: instance.onSuccess, - back(name) { - instance.editingSound.set(); - - if (name != null) { - const sound = instance.sound.get(); - if (sound.name != null && sound.name !== name) { - return instance.loadedName.set(name); - } - } - }, - }; - }, -}); - -Template.soundInfo.events({ - 'click .delete'(e, instance) { - e.stopPropagation(); - e.preventDefault(); - const sound = instance.sound.get(); - if (sound != null) { - const { _id } = sound; - modal.open({ - title: t('Are_you_sure'), - text: t('Custom_Sound_Delete_Warning'), - type: 'warning', - showCancelButton: true, - confirmButtonColor: '#DD6B55', - confirmButtonText: t('Yes_delete_it'), - cancelButtonText: t('Cancel'), - closeOnConfirm: false, - html: false, - }, function() { - Meteor.call('deleteCustomSound', _id, (error/* , result*/) => { - if (error) { - handleError(error); - } else { - modal.open({ - title: t('Deleted'), - text: t('Custom_Sound_Has_Been_Deleted'), - type: 'success', - timer: 2000, - showConfirmButton: false, - }); - instance.onSuccess(); - instance.data.tabBar.showGroup('custom-sounds'); - instance.data.tabBar.close(); - } - }); - }); - } - }, - - 'click .edit-sound'(e, instance) { - e.stopPropagation(); - e.preventDefault(); - - instance.editingSound.set(instance.sound.get()._id); - }, -}); - -Template.soundInfo.onCreated(function() { - this.sound = new ReactiveVar(); - - this.editingSound = new ReactiveVar(); - - this.loadedName = new ReactiveVar(); - this.onSuccess = Template.currentData().onSuccess; - - this.autorun(() => { - const data = Template.currentData(); - if (data && data.clear != null) { - this.clear = data.clear; - } - }); - - this.autorun(() => { - const data = Template.currentData().sound; - const sound = this.sound.get(); - if (sound && sound.name != null) { - this.loadedName.set(sound.name); - } else if (data.name != null) { - this.loadedName.set(data.name); - } - }); - - this.autorun(() => { - const data = Template.currentData().sound; - this.sound.set(data); - }); -}); diff --git a/app/custom-sounds/client/admin/startup.js b/app/custom-sounds/client/admin/startup.js deleted file mode 100644 index 2647b17ea42..00000000000 --- a/app/custom-sounds/client/admin/startup.js +++ /dev/null @@ -1,11 +0,0 @@ -import { hasAtLeastOnePermission } from '../../../authorization'; -import { registerAdminSidebarItem } from '../../../ui-admin/client'; - -registerAdminSidebarItem({ - href: 'custom-sounds', - i18nLabel: 'Custom_Sounds', - icon: 'volume', - permissionGranted() { - return hasAtLeastOnePermission(['manage-sounds']); - }, -}); diff --git a/app/custom-sounds/client/admin/views.js b/app/custom-sounds/client/admin/views.js deleted file mode 100644 index 3a2397b2bda..00000000000 --- a/app/custom-sounds/client/admin/views.js +++ /dev/null @@ -1,8 +0,0 @@ -import './adminSoundEdit.html'; -import './adminSoundInfo.html'; -import './adminSounds.html'; -import './adminSounds'; -import './soundEdit.html'; -import './soundEdit'; -import './soundInfo.html'; -import './soundInfo'; diff --git a/app/custom-sounds/client/index.js b/app/custom-sounds/client/index.js index eb17166ef2b..77706dca50e 100644 --- a/app/custom-sounds/client/index.js +++ b/app/custom-sounds/client/index.js @@ -1,7 +1,4 @@ import './notifications/deleteCustomSound'; import './notifications/updateCustomSound'; -import './admin/route'; -import './admin/startup'; -import '../assets/stylesheets/customSoundsAdmin.css'; export { CustomSounds } from './lib/CustomSounds'; diff --git a/app/custom-sounds/client/lib/CustomSounds.js b/app/custom-sounds/client/lib/CustomSounds.js index 09c175ac6c0..a0422581f6a 100644 --- a/app/custom-sounds/client/lib/CustomSounds.js +++ b/app/custom-sounds/client/lib/CustomSounds.js @@ -4,6 +4,8 @@ import _ from 'underscore'; import { CachedCollectionManager } from '../../../ui-cached-collection'; +const getCustomSoundId = (sound) => `custom-sound-${ sound }`; + class CustomSoundsClass { constructor() { this.list = new ReactiveVar({}); @@ -19,7 +21,7 @@ class CustomSoundsClass { if (!sound.src) { sound.src = this.getURL(sound); } - const audio = $('