mirror of
https://github.com/discourse/discourse.git
synced 2025-02-16 23:32:44 +08:00
DEV: Convert admin emojis UI to new layout
This commit is contained in:
parent
75ee73f969
commit
64ab6d8a5c
|
@ -0,0 +1,65 @@
|
|||
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 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: () => {
|
||||
return ajax("/admin/customize/emojis/" + emoji.get("name"), {
|
||||
type: "DELETE",
|
||||
}).then(() => {
|
||||
this.model.removeObject(emoji);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
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;
|
||||
|
||||
@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;
|
||||
this.model.pushObject(EmberObject.create(emoji));
|
||||
this.router.transitionTo("adminEmojis.index");
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 {}
|
||||
|
|
|
@ -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",
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<AdminPageSubheader @titleLabel="admin.emoji.title">
|
||||
<:actions as |actions|>
|
||||
<actions.Primary
|
||||
@route="adminEmojis.new"
|
||||
@label="admin.emoji.new"
|
||||
/>
|
||||
</:actions>
|
||||
</AdminPageSubheader>
|
||||
|
||||
<div class="form-horizontal">
|
||||
<div class="inline-form">
|
||||
<label class="label">Show</label>
|
||||
<ComboBox
|
||||
@value={{this.filter}}
|
||||
@content={{this.sortingGroups}}
|
||||
@nameProperty={{null}}
|
||||
@valueProperty={{null}}
|
||||
@onChange={{action "filterGroups"}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if this.sortedEmojis}}
|
||||
<table id="custom_emoji" class="d-admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n "admin.emoji.image"}}</th>
|
||||
<th>{{i18n "admin.emoji.name"}}</th>
|
||||
<th>{{i18n "admin.emoji.group"}}</th>
|
||||
<th colspan="3">{{i18n "admin.emoji.created_by"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each this.sortedEmojis as |e|}}
|
||||
<tr class="d-admin-row__content">
|
||||
<td class="d-admin-row__overview">
|
||||
<img
|
||||
class="emoji emoji-custom"
|
||||
src={{e.url}}
|
||||
title={{e.name}}
|
||||
alt={{i18n "admin.emoji.alt"}}
|
||||
/>
|
||||
</td>
|
||||
<td class="d-admin-row__detail">
|
||||
<div class="d-admin-row__mobile-label">
|
||||
{{i18n "admin.emoji.name"}}
|
||||
</div>
|
||||
:{{e.name}}:
|
||||
</td>
|
||||
<td class="d-admin-row__detail">
|
||||
<div class="d-admin-row__mobile-label">
|
||||
{{i18n "admin.emoji.group"}}
|
||||
</div>
|
||||
{{e.group}}
|
||||
</td>
|
||||
<td class="d-admin-row__detail">
|
||||
<div class="d-admin-row__mobile-label">
|
||||
{{i18n "admin.emoji.created_by"}}
|
||||
</div>
|
||||
{{e.created_by}}
|
||||
</td>
|
||||
<td class="d-admin-row__controls action">
|
||||
<DButton
|
||||
@action={{fn this.destroyEmoji e}}
|
||||
@icon="trash-can"
|
||||
class="btn-danger"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{/if}}
|
|
@ -0,0 +1,4 @@
|
|||
<EmojiUploader
|
||||
@emojiGroups={{this.emojiGroups}}
|
||||
@done={{action "emojiUploaded"}}
|
||||
/>
|
|
@ -0,0 +1,9 @@
|
|||
<DBreadcrumbsItem @path="/admin/emojis/settings" @label={{i18n "settings"}} />
|
||||
|
||||
<div class="content-body admin-config-area__settings admin-detail pull-left">
|
||||
<AdminFilteredSiteSettings
|
||||
@initialFilter={{@model.initialFilter}}
|
||||
@settings={{@model.settings}}
|
||||
@onFilterChanged={{this.filterChanged}}
|
||||
/>
|
||||
</div>
|
|
@ -1,86 +1,29 @@
|
|||
<div class="admin-emojis">
|
||||
<div class="admin-emojis__header">
|
||||
<h1>{{i18n "admin.emoji.title"}}</h1>
|
||||
<LinkTo
|
||||
@route="adminSiteSettingsCategory"
|
||||
@model="all_results"
|
||||
@query={{hash filter="emoji"}}
|
||||
>
|
||||
{{i18n "admin.emoji.settings"}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
|
||||
<p class="desc">{{i18n "admin.emoji.help"}}</p>
|
||||
|
||||
<EmojiUploader
|
||||
@emojiGroups={{this.emojiGroups}}
|
||||
@done={{action "emojiUploaded"}}
|
||||
/>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="form-horizontal">
|
||||
<div class="inline-form">
|
||||
<label class="label">Show</label>
|
||||
<ComboBox
|
||||
@value={{this.filter}}
|
||||
@content={{this.sortingGroups}}
|
||||
@nameProperty={{null}}
|
||||
@valueProperty={{null}}
|
||||
@onChange={{action "filterGroups"}}
|
||||
<div class="admin-emojis admin-config-page">
|
||||
<AdminPageHeader
|
||||
@titleLabel="admin.emoji.title"
|
||||
@descriptionLabel="admin.emoji.description"
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/customize/emoji"
|
||||
@label={{i18n "admin.emoji.title"}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</:breadcrumbs>
|
||||
<:tabs>
|
||||
<NavItem
|
||||
@route="adminEmojis.settings"
|
||||
@label="settings"
|
||||
class="admin-emojis-tabs__settings"
|
||||
/>
|
||||
<NavItem
|
||||
@route="adminEmojis.index"
|
||||
@label="admin.emoji.title"
|
||||
class="admin-emojis-tabs__emoji"
|
||||
/>
|
||||
</:tabs>
|
||||
</AdminPageHeader>
|
||||
|
||||
{{#if this.sortedEmojis}}
|
||||
<table id="custom_emoji" class="d-admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n "admin.emoji.image"}}</th>
|
||||
<th>{{i18n "admin.emoji.name"}}</th>
|
||||
<th>{{i18n "admin.emoji.group"}}</th>
|
||||
<th colspan="3">{{i18n "admin.emoji.created_by"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each this.sortedEmojis as |e|}}
|
||||
<tr class="d-admin-row__content">
|
||||
<td class="d-admin-row__overview">
|
||||
<img
|
||||
class="emoji emoji-custom"
|
||||
src={{e.url}}
|
||||
title={{e.name}}
|
||||
alt={{i18n "admin.emoji.alt"}}
|
||||
/>
|
||||
</td>
|
||||
<td class="d-admin-row__detail">
|
||||
<div class="d-admin-row__mobile-label">
|
||||
{{i18n "admin.emoji.name"}}
|
||||
</div>
|
||||
:{{e.name}}:
|
||||
</td>
|
||||
<td class="d-admin-row__detail">
|
||||
<div class="d-admin-row__mobile-label">
|
||||
{{i18n "admin.emoji.group"}}
|
||||
</div>
|
||||
{{e.group}}
|
||||
</td>
|
||||
<td class="d-admin-row__detail">
|
||||
<div class="d-admin-row__mobile-label">
|
||||
{{i18n "admin.emoji.created_by"}}
|
||||
</div>
|
||||
{{e.created_by}}
|
||||
</td>
|
||||
<td class="d-admin-row__controls action">
|
||||
<DButton
|
||||
@action={{fn this.destroyEmoji e}}
|
||||
@icon="trash-can"
|
||||
class="btn-danger"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{/if}}
|
||||
<div class="admin-container admin-config-page__main-area">
|
||||
{{outlet}}
|
||||
</div>
|
||||
</div>
|
|
@ -7199,7 +7199,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…"
|
||||
|
|
7
spec/fabricators/custom_emoji_fabricator.rb
Normal file
7
spec/fabricators/custom_emoji_fabricator.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Fabricator(:custom_emoji) do
|
||||
upload { Fabricate(:image_upload) }
|
||||
|
||||
name { "joffrey_facepalm" }
|
||||
end
|
33
spec/system/admin_customize_emojis_spec.rb
Normal file
33
spec/system/admin_customize_emojis_spec.rb
Normal file
|
@ -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
|
43
spec/system/page_objects/admin_emojis.rb
Normal file
43
spec/system/page_objects/admin_emojis.rb
Normal file
|
@ -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
|
Loading…
Reference in New Issue
Block a user