diff --git a/app/assets/javascripts/admin/addon/controllers/admin-emojis-index.js b/app/assets/javascripts/admin/addon/controllers/admin-emojis-index.js new file mode 100644 index 00000000000..37db1b2f5c4 --- /dev/null +++ b/app/assets/javascripts/admin/addon/controllers/admin-emojis-index.js @@ -0,0 +1,72 @@ +import Controller from "@ember/controller"; +import { action, computed } from "@ember/object"; +import { sort } from "@ember/object/computed"; +import { service } from "@ember/service"; +import { ajax } from "discourse/lib/ajax"; +import { popupAjaxError } from "discourse/lib/ajax-error"; +import I18n from "discourse-i18n"; + +const ALL_FILTER = "all"; + +export default class AdminEmojisIndexController extends Controller { + @service dialog; + + filter = null; + sorting = null; + + @sort("filteredEmojis.[]", "sorting") sortedEmojis; + + init() { + super.init(...arguments); + + this.setProperties({ + filter: ALL_FILTER, + sorting: ["group", "name"], + }); + } + + @computed("model.[]", "filter") + get filteredEmojis() { + if (!this.filter || this.filter === ALL_FILTER) { + return this.model; + } else { + return this.model.filterBy("group", this.filter); + } + } + + @computed("model.[]") + get emojiGroups() { + return this.model.mapBy("group").uniq(); + } + + @computed("emojiGroups.[]") + get sortingGroups() { + return [ALL_FILTER].concat(this.emojiGroups); + } + + @action + filterGroups(value) { + this.set("filter", value); + } + + @action + destroyEmoji(emoji) { + this.dialog.yesNoConfirm({ + message: I18n.t("admin.emoji.delete_confirm", { + name: emoji.get("name"), + }), + didConfirm: () => this.#destroyEmoji(emoji), + }); + } + + async #destroyEmoji(emoji) { + try { + await ajax("/admin/customize/emojis/" + emoji.get("name"), { + type: "DELETE", + }); + this.model.removeObject(emoji); + } catch (err) { + popupAjaxError(err); + } + } +} diff --git a/app/assets/javascripts/admin/addon/controllers/admin-emojis-new.js b/app/assets/javascripts/admin/addon/controllers/admin-emojis-new.js new file mode 100644 index 00000000000..9256b9a2921 --- /dev/null +++ b/app/assets/javascripts/admin/addon/controllers/admin-emojis-new.js @@ -0,0 +1,29 @@ +import Controller from "@ember/controller"; +import EmberObject, { action, computed } from "@ember/object"; +import { service } from "@ember/service"; + +const ALL_FILTER = "all"; + +export default class AdminEmojisNewController extends Controller { + @service router; + @service currentUser; + + @computed("model") + get emojiGroups() { + return this.model.mapBy("group").uniq(); + } + + @computed("emojiGroups.[]") + get sortingGroups() { + return [ALL_FILTER].concat(this.emojiGroups); + } + + @action + emojiUploaded(emoji, group) { + emoji.url += "?t=" + new Date().getTime(); + emoji.group = group; + emoji.created_by = this.currentUser.username; + this.model.pushObject(EmberObject.create(emoji)); + this.router.transitionTo("adminEmojis.index"); + } +} diff --git a/app/assets/javascripts/admin/addon/controllers/admin-emojis-settings.js b/app/assets/javascripts/admin/addon/controllers/admin-emojis-settings.js new file mode 100644 index 00000000000..dad5d1cab79 --- /dev/null +++ b/app/assets/javascripts/admin/addon/controllers/admin-emojis-settings.js @@ -0,0 +1,11 @@ +import Controller from "@ember/controller"; +import { action } from "@ember/object"; + +export default class AdminEmojisSettingsController extends Controller { + filter = ""; + + @action + filterChanged(filterData) { + this.set("filter", filterData.filter); + } +} diff --git a/app/assets/javascripts/admin/addon/controllers/admin-emojis.js b/app/assets/javascripts/admin/addon/controllers/admin-emojis.js index 0533c382754..341a141a9ab 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-emojis.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-emojis.js @@ -1,86 +1,3 @@ import Controller from "@ember/controller"; -import EmberObject, { action, computed } from "@ember/object"; -import { sort } from "@ember/object/computed"; -import { service } from "@ember/service"; -import { ajax } from "discourse/lib/ajax"; -import I18n from "discourse-i18n"; -const ALL_FILTER = "all"; - -export default class AdminEmojisController extends Controller { - @service dialog; - - filter = null; - sorting = null; - - @sort("filteredEmojis.[]", "sorting") sortedEmojis; - init() { - super.init(...arguments); - - this.setProperties({ - filter: ALL_FILTER, - sorting: ["group", "name"], - }); - } - - @computed("model") - get emojiGroups() { - return this.model.mapBy("group").uniq(); - } - - @computed("emojiGroups.[]") - get sortingGroups() { - return [ALL_FILTER].concat(this.emojiGroups); - } - - @computed("model.[]", "filter") - get filteredEmojis() { - if (!this.filter || this.filter === ALL_FILTER) { - return this.model; - } else { - return this.model.filterBy("group", this.filter); - } - } - - _highlightEmojiList() { - const customEmojiListEl = document.querySelector("#custom_emoji"); - if ( - customEmojiListEl && - !customEmojiListEl.classList.contains("highlighted") - ) { - customEmojiListEl.classList.add("highlighted"); - customEmojiListEl.addEventListener("animationend", () => { - customEmojiListEl.classList.remove("highlighted"); - }); - } - } - - @action - filterGroups(value) { - this.set("filter", value); - } - - @action - emojiUploaded(emoji, group) { - emoji.url += "?t=" + new Date().getTime(); - emoji.group = group; - this.model.pushObject(EmberObject.create(emoji)); - this._highlightEmojiList(); - } - - @action - destroyEmoji(emoji) { - this.dialog.yesNoConfirm({ - message: I18n.t("admin.emoji.delete_confirm", { - name: emoji.get("name"), - }), - didConfirm: () => { - return ajax("/admin/customize/emojis/" + emoji.get("name"), { - type: "DELETE", - }).then(() => { - this.model.removeObject(emoji); - }); - }, - }); - } -} +export default class AdminEmojisController extends Controller {} diff --git a/app/assets/javascripts/admin/addon/routes/admin-emojis-settings.js b/app/assets/javascripts/admin/addon/routes/admin-emojis-settings.js new file mode 100644 index 00000000000..d618bbc46c0 --- /dev/null +++ b/app/assets/javascripts/admin/addon/routes/admin-emojis-settings.js @@ -0,0 +1,20 @@ +import DiscourseRoute from "discourse/routes/discourse"; +import I18n from "discourse-i18n"; +import SiteSetting from "admin/models/site-setting"; + +export default class AdminEmojisSettingsRoute extends DiscourseRoute { + queryParams = { + filter: { replace: true }, + }; + + titleToken() { + return I18n.t("settings"); + } + + async model() { + return { + settings: await SiteSetting.findAll(), + initialFilter: "emoji", + }; + } +} diff --git a/app/assets/javascripts/admin/addon/routes/admin-emojis.js b/app/assets/javascripts/admin/addon/routes/admin-emojis.js index 0fa0095cb4d..c5433ed3dcd 100644 --- a/app/assets/javascripts/admin/addon/routes/admin-emojis.js +++ b/app/assets/javascripts/admin/addon/routes/admin-emojis.js @@ -1,8 +1,13 @@ import EmberObject from "@ember/object"; import { ajax } from "discourse/lib/ajax"; import DiscourseRoute from "discourse/routes/discourse"; +import I18n from "discourse-i18n"; export default class AdminEmojisRoute extends DiscourseRoute { + titleToken() { + return I18n.t("admin.emoji.title"); + } + async model() { const emojis = await ajax("/admin/customize/emojis.json"); return emojis.map((emoji) => EmberObject.create(emoji)); diff --git a/app/assets/javascripts/admin/addon/routes/admin-route-map.js b/app/assets/javascripts/admin/addon/routes/admin-route-map.js index facec60a4f4..6c76f9b7098 100644 --- a/app/assets/javascripts/admin/addon/routes/admin-route-map.js +++ b/app/assets/javascripts/admin/addon/routes/admin-route-map.js @@ -72,7 +72,15 @@ export default function () { path: "/user_fields", resetNamespace: true, }); - this.route("adminEmojis", { path: "/emojis", resetNamespace: true }); + this.route( + "adminEmojis", + { path: "/emojis", resetNamespace: true }, + function () { + this.route("new"); + this.route("index", { path: "/" }); + this.route("settings"); + } + ); this.route("adminPermalinks", { path: "/permalinks", resetNamespace: true, diff --git a/app/assets/javascripts/admin/addon/templates/emojis-index.hbs b/app/assets/javascripts/admin/addon/templates/emojis-index.hbs new file mode 100644 index 00000000000..ba0718ba326 --- /dev/null +++ b/app/assets/javascripts/admin/addon/templates/emojis-index.hbs @@ -0,0 +1,63 @@ +
+
+ +
+
+ +{{#if this.sortedEmojis}} + + + + + + + + + + + {{#each this.sortedEmojis as |emoji|}} + + + + + + + + {{/each}} + +
{{i18n "admin.emoji.image"}}{{i18n "admin.emoji.name"}}{{i18n "admin.emoji.group"}}{{i18n "admin.emoji.created_by"}}
+ {{i18n + +
+ {{i18n "admin.emoji.name"}} +
+ :{{emoji.name}}: +
+
+ {{i18n "admin.emoji.group"}} +
+ {{emoji.group}} +
+
+ {{i18n "admin.emoji.created_by"}} +
+ {{emoji.created_by}} +
+ +
+{{/if}} \ No newline at end of file diff --git a/app/assets/javascripts/admin/addon/templates/emojis-new.hbs b/app/assets/javascripts/admin/addon/templates/emojis-new.hbs new file mode 100644 index 00000000000..e0d887971c1 --- /dev/null +++ b/app/assets/javascripts/admin/addon/templates/emojis-new.hbs @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/app/assets/javascripts/admin/addon/templates/emojis-settings.hbs b/app/assets/javascripts/admin/addon/templates/emojis-settings.hbs new file mode 100644 index 00000000000..09cccb4308a --- /dev/null +++ b/app/assets/javascripts/admin/addon/templates/emojis-settings.hbs @@ -0,0 +1,9 @@ + + +
+ +
\ No newline at end of file diff --git a/app/assets/javascripts/admin/addon/templates/emojis.hbs b/app/assets/javascripts/admin/addon/templates/emojis.hbs index 562786cd65c..6d0d2fac386 100644 --- a/app/assets/javascripts/admin/addon/templates/emojis.hbs +++ b/app/assets/javascripts/admin/addon/templates/emojis.hbs @@ -1,86 +1,32 @@ -
-
-

{{i18n "admin.emoji.title"}}

- - {{i18n "admin.emoji.settings"}} - -
- -

{{i18n "admin.emoji.help"}}

- - - -
- -
-
- - + + <:breadcrumbs> + -
-
+ + <:actions as |actions|> + + + <:tabs> + + + + - {{#if this.sortedEmojis}} - - - - - - - - - - - {{#each this.sortedEmojis as |e|}} - - - - - - - - {{/each}} - -
{{i18n "admin.emoji.image"}}{{i18n "admin.emoji.name"}}{{i18n "admin.emoji.group"}}{{i18n "admin.emoji.created_by"}}
- {{i18n - -
- {{i18n "admin.emoji.name"}} -
- :{{e.name}}: -
-
- {{i18n "admin.emoji.group"}} -
- {{e.group}} -
-
- {{i18n "admin.emoji.created_by"}} -
- {{e.created_by}} -
- -
- {{/if}} +
+ {{outlet}} +
\ No newline at end of file diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 3a62d47deb7..cfccb3dc1f8 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -7204,7 +7204,8 @@ en: emoji: title: "Emoji" - help: "Add new emoji that will be available to everyone. Drag and drop multiple files at once without entering a name to create emojis using their file names. The selected group will be used for all files that are added at the same time. You can also click 'Add New Emoji' to open the file picker." + description: "Add new emoji that will be available to everyone. Drag and drop multiple files at once without entering a name to create emojis using their file names. The selected group will be used for all files that are added at the same time. You can also click 'Add New Emoji' to open the file picker." + new: "Add" add: "Add New Emoji" choose_files: "Choose Files" uploading: "Uploading…" diff --git a/config/routes.rb b/config/routes.rb index 66dc52052bc..64bf80192d6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -246,6 +246,8 @@ Discourse::Application.routes.draw do only: %i[index create update destroy], constraints: AdminConstraint.new resources :emojis, only: %i[index create destroy], constraints: AdminConstraint.new + get "emojis/new" => "emojis#index" + get "emojis/settings" => "emojis#index" resources :form_templates, constraints: AdminConstraint.new, path: "/form-templates" do collection { get "preview" => "form_templates#preview" } end diff --git a/spec/fabricators/custom_emoji_fabricator.rb b/spec/fabricators/custom_emoji_fabricator.rb new file mode 100644 index 00000000000..fe3eee4b11b --- /dev/null +++ b/spec/fabricators/custom_emoji_fabricator.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +Fabricator(:custom_emoji) do + upload { Fabricate(:image_upload) } + + name { "joffrey_facepalm" } +end diff --git a/spec/system/admin_customize_emojis_spec.rb b/spec/system/admin_customize_emojis_spec.rb new file mode 100644 index 00000000000..370cb4437fd --- /dev/null +++ b/spec/system/admin_customize_emojis_spec.rb @@ -0,0 +1,33 @@ +#frozen_string_literal: true + +describe "Admin Customize Emoji Page", type: :system do + fab!(:current_user) { Fabricate(:admin) } + + let(:emojis_page) { PageObjects::Pages::AdminEmojis.new } + let(:dialog) { PageObjects::Components::Dialog.new } + let(:settings_page) { PageObjects::Pages::AdminSiteSettings.new } + + before do + Fabricate(:custom_emoji) + + sign_in(current_user) + end + + it "shows a list of custom emojis" do + emojis_page.visit_page + expect(emojis_page).to have_emoji_listed("joffrey_facepalm") + end + + it "can delete a custom emoji" do + emojis_page.visit_page + emojis_page.delete_emoji("joffrey_facepalm") + dialog.click_yes + expect(emojis_page).to have_no_emoji_listed("joffrey_facepalm") + end + + it "can see emoji site settings" do + emojis_page.visit_page + emojis_page.click_tab("settings") + expect(settings_page).to have_setting("enable_emoji") + end +end diff --git a/spec/system/page_objects/admin_emojis.rb b/spec/system/page_objects/admin_emojis.rb new file mode 100644 index 00000000000..924a9878fca --- /dev/null +++ b/spec/system/page_objects/admin_emojis.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module PageObjects + module Pages + class AdminEmojis < PageObjects::Pages::Base + def visit_page + page.visit "/admin/customize/emojis" + self + end + + def click_tab(tab_name) + case tab_name + when "settings" + find(".admin-emojis-tabs__settings").click + when "index" + find(".admin-emojis-tabs__emoji").click + end + end + + def has_emoji_listed?(name) + page.has_css?(emoji_table_selector, text: name) + end + + def has_no_emoji_listed?(name) + page.has_no_css?(emoji_table_selector, text: name) + end + + def delete_emoji(name) + find(".d-admin-row__content", text: name).find(delete_button_selector).click + end + + private + + def emoji_table_selector + "#custom_emoji" + end + + def delete_button_selector + ".d-icon-trash-can" + end + end + end +end