refactor parse-from-toot
This commit is contained in:
parent
beb09aa827
commit
59413c3482
|
@ -29,11 +29,15 @@ const {
|
||||||
useCheckListInput
|
useCheckListInput
|
||||||
} = require("../../../lib/form");
|
} = require("../../../lib/form");
|
||||||
|
|
||||||
|
const useFormSubmit = require("../../../lib/form/submit");
|
||||||
|
|
||||||
const CheckList = require("../../../components/check-list");
|
const CheckList = require("../../../components/check-list");
|
||||||
const { CategorySelect } = require('../category-select');
|
const { CategorySelect } = require('../category-select');
|
||||||
|
|
||||||
const query = require("../../../lib/query");
|
const query = require("../../../lib/query");
|
||||||
const Loading = require("../../../components/loading");
|
const Loading = require("../../../components/loading");
|
||||||
|
const { TextInput } = require("../../../components/form/inputs");
|
||||||
|
const MutationButton = require("../../../components/form/mutation-button");
|
||||||
|
|
||||||
module.exports = function ParseFromToot({ emojiCodes }) {
|
module.exports = function ParseFromToot({ emojiCodes }) {
|
||||||
const [searchStatus, { data, isLoading, isSuccess, error }] = query.useSearchStatusForEmojiMutation();
|
const [searchStatus, { data, isLoading, isSuccess, error }] = query.useSearchStatusForEmojiMutation();
|
||||||
|
@ -70,7 +74,9 @@ module.exports = function ParseFromToot({ emojiCodes }) {
|
||||||
|
|
||||||
function submitSearch(e) {
|
function submitSearch(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
searchStatus(url);
|
if (url.trim().length != 0) {
|
||||||
|
searchStatus(url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -91,7 +97,7 @@ module.exports = function ParseFromToot({ emojiCodes }) {
|
||||||
/>
|
/>
|
||||||
<button className="button-inline" disabled={isLoading}>
|
<button className="button-inline" disabled={isLoading}>
|
||||||
<i className={[
|
<i className={[
|
||||||
"fa",
|
"fa fa-fw",
|
||||||
(isLoading
|
(isLoading
|
||||||
? "fa-refresh fa-spin"
|
? "fa-refresh fa-spin"
|
||||||
: "fa-search")
|
: "fa-search")
|
||||||
|
@ -99,7 +105,6 @@ module.exports = function ParseFromToot({ emojiCodes }) {
|
||||||
<span className="sr-only">Search</span>
|
<span className="sr-only">Search</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{isLoading && <Loading />}
|
|
||||||
{error && <div className="error">{error.data.error}</div>}
|
{error && <div className="error">{error.data.error}</div>}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -119,22 +124,25 @@ function CopyEmojiForm({ localEmojiCodes, type, domain, emojiList }) {
|
||||||
|
|
||||||
const [categoryState, resetCategory, { category }] = useComboBoxInput("category");
|
const [categoryState, resetCategory, { category }] = useComboBoxInput("category");
|
||||||
|
|
||||||
|
const buttonsInactive = emojiCheckList.someSelected
|
||||||
|
? {}
|
||||||
|
: {
|
||||||
|
disabled: true,
|
||||||
|
title: "No emoji selected, cannot perform any actions"
|
||||||
|
};
|
||||||
|
|
||||||
function submit(action) {
|
function submit(action) {
|
||||||
Promise.try(() => {
|
Promise.try(() => {
|
||||||
setError(null);
|
setError(null);
|
||||||
const selectedShortcodes = syncpipe(emojiCheckList.value, [
|
const selectedShortcodes = emojiCheckList.selectedValues.map(([shortcode, entry]) => {
|
||||||
(_) => Object.entries(_),
|
if (action == "copy" && !entry.valid) {
|
||||||
(_) => _.filter(([_shortcode, entry]) => entry.checked),
|
throw `One or more selected emoji have non-unique shortcodes (${shortcode}), unselect them or pick a different local shortcode`;
|
||||||
(_) => _.map(([shortcode, entry]) => {
|
}
|
||||||
if (action == "copy" && !entry.valid) {
|
return {
|
||||||
throw `One or more selected emoji have non-unique shortcodes (${shortcode}), unselect them or pick a different local shortcode`;
|
shortcode,
|
||||||
}
|
localShortcode: entry.shortcode
|
||||||
return {
|
};
|
||||||
shortcode,
|
});
|
||||||
localShortcode: entry.shortcode
|
|
||||||
};
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
|
|
||||||
return patchRemoteEmojis({
|
return patchRemoteEmojis({
|
||||||
action,
|
action,
|
||||||
|
@ -166,15 +174,6 @@ function CopyEmojiForm({ localEmojiCodes, type, domain, emojiList }) {
|
||||||
Component={EmojiEntry}
|
Component={EmojiEntry}
|
||||||
localEmojiCodes={localEmojiCodes}
|
localEmojiCodes={localEmojiCodes}
|
||||||
/>
|
/>
|
||||||
{/* {emojiList.map((emoji) => (
|
|
||||||
<EmojiEntry
|
|
||||||
key={emoji.shortcode}
|
|
||||||
emoji={emoji}
|
|
||||||
localEmojiCodes={localEmojiCodes}
|
|
||||||
updateEmoji={(value) => updateEmoji(emoji.shortcode, value)}
|
|
||||||
checked={emojiState[emoji.shortcode].checked}
|
|
||||||
/>
|
|
||||||
))} */}
|
|
||||||
|
|
||||||
<CategorySelect
|
<CategorySelect
|
||||||
value={category}
|
value={category}
|
||||||
|
@ -182,8 +181,8 @@ function CopyEmojiForm({ localEmojiCodes, type, domain, emojiList }) {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="action-buttons row">
|
<div className="action-buttons row">
|
||||||
<button disabled={!emojiCheckList.someSelected} onClick={() => submit("copy")}>{patchResult.isLoading ? "Processing..." : "Copy to local emoji"}</button>
|
<MutationButton label="Copy to local emoji" type="button" result={patchResult} {...buttonsInactive} />
|
||||||
<button disabled={!emojiCheckList.someSelected} onClick={() => submit("disable")} className="danger">{patchResult.isLoading ? "Processing..." : "Disable"}</button>
|
<MutationButton label="Disable" type="button" result={patchResult} className="button danger" {...buttonsInactive} />
|
||||||
</div>
|
</div>
|
||||||
{err && <div className="error">
|
{err && <div className="error">
|
||||||
{err}
|
{err}
|
||||||
|
@ -196,7 +195,7 @@ function CopyEmojiForm({ localEmojiCodes, type, domain, emojiList }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function EmojiEntry({ entry: emoji, localEmojiCodes, onChange }) {
|
function EmojiEntry({ entry: emoji, localEmojiCodes, onChange }) {
|
||||||
const [onShortcodeChange, _resetShortcode, { shortcode, shortcodeRef, shortcodeValid }] = useTextInput("shortcode", {
|
const shortcodeField = useTextInput("shortcode", {
|
||||||
defaultValue: emoji.shortcode,
|
defaultValue: emoji.shortcode,
|
||||||
validator: function validateShortcode(code) {
|
validator: function validateShortcode(code) {
|
||||||
return (emoji.checked && localEmojiCodes.has(code))
|
return (emoji.checked && localEmojiCodes.has(code))
|
||||||
|
@ -206,24 +205,20 @@ function EmojiEntry({ entry: emoji, localEmojiCodes, onChange }) {
|
||||||
});
|
});
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
onChange({ valid: shortcodeValid });
|
onChange({ valid: shortcodeField.valid });
|
||||||
/* eslint-disable-next-line react-hooks/exhaustive-deps */
|
/* eslint-disable-next-line react-hooks/exhaustive-deps */
|
||||||
}, [shortcodeValid]);
|
}, [shortcodeField.valid]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<img className="emoji" src={emoji.url} title={emoji.shortcode} />
|
<img className="emoji" src={emoji.url} title={emoji.shortcode} />
|
||||||
|
|
||||||
<input
|
<TextInput
|
||||||
type="text"
|
field={shortcodeField}
|
||||||
id="shortcode"
|
|
||||||
name="Shortcode"
|
|
||||||
ref={shortcodeRef}
|
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
onShortcodeChange(e);
|
shortcodeField.onChange(e);
|
||||||
onChange({ shortcode: e.target.value, checked: true });
|
onChange({ shortcode: e.target.value, checked: true });
|
||||||
}}
|
}}
|
||||||
value={shortcode}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -22,7 +22,7 @@ const React = require("react");
|
||||||
|
|
||||||
module.exports = function CheckList({ field, Component, ...componentProps }) {
|
module.exports = function CheckList({ field, Component, ...componentProps }) {
|
||||||
return (
|
return (
|
||||||
<div className="checkbox-list">
|
<div className="checkbox-list list">
|
||||||
<label className="header">
|
<label className="header">
|
||||||
<input
|
<input
|
||||||
ref={field.toggleAll.ref}
|
ref={field.toggleAll.ref}
|
||||||
|
@ -46,10 +46,10 @@ module.exports = function CheckList({ field, Component, ...componentProps }) {
|
||||||
|
|
||||||
function CheckListEntry({ entry, onChange, Component, componentProps }) {
|
function CheckListEntry({ entry, onChange, Component, componentProps }) {
|
||||||
return (
|
return (
|
||||||
<label className="row">
|
<label className="entry">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
onChange={(e) => onChange({ checked: e.target.value })}
|
onChange={(e) => onChange({ checked: e.target.checked })}
|
||||||
checked={entry.checked}
|
checked={entry.checked}
|
||||||
/>
|
/>
|
||||||
<Component entry={entry} onChange={onChange} {...componentProps} />
|
<Component entry={entry} onChange={onChange} {...componentProps} />
|
||||||
|
|
|
@ -23,8 +23,6 @@ const React = require("react");
|
||||||
module.exports = function MutationButton({ label, result, disabled, ...inputProps }) {
|
module.exports = function MutationButton({ label, result, disabled, ...inputProps }) {
|
||||||
let iconClass = "";
|
let iconClass = "";
|
||||||
|
|
||||||
console.log(label, result);
|
|
||||||
|
|
||||||
if (result.isLoading) {
|
if (result.isLoading) {
|
||||||
iconClass = "fa-spin fa-refresh";
|
iconClass = "fa-spin fa-refresh";
|
||||||
} else if (result.isSuccess) {
|
} else if (result.isSuccess) {
|
||||||
|
@ -36,7 +34,7 @@ module.exports = function MutationButton({ label, result, disabled, ...inputProp
|
||||||
<section className="error">{result.error.status}: {result.error.data.error}</section>
|
<section className="error">{result.error.status}: {result.error.data.error}</section>
|
||||||
}
|
}
|
||||||
<button type="submit" disabled={result.isLoading || disabled} {...inputProps}>
|
<button type="submit" disabled={result.isLoading || disabled} {...inputProps}>
|
||||||
<i className={`fa fa-fw ${iconClass}`} aria-hidden="true"></i>
|
<i className={`fa fa-fw with-text ${iconClass}`} aria-hidden="true"></i>
|
||||||
{result.isLoading
|
{result.isLoading
|
||||||
? "Processing..."
|
? "Processing..."
|
||||||
: label
|
: label
|
||||||
|
|
|
@ -120,6 +120,13 @@ module.exports = function useCheckListInput({ name, Name }, { entries, uniqueKey
|
||||||
setState(updateAllState(state, defaultValue));
|
setState(updateAllState(state, defaultValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selectedValues() {
|
||||||
|
return syncpipe(state, [
|
||||||
|
(_) => Object.values(_),
|
||||||
|
(_) => _.filter((entry) => entry.checked)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
return Object.assign([
|
return Object.assign([
|
||||||
state,
|
state,
|
||||||
reset,
|
reset,
|
||||||
|
@ -128,6 +135,7 @@ module.exports = function useCheckListInput({ name, Name }, { entries, uniqueKey
|
||||||
name,
|
name,
|
||||||
value: state,
|
value: state,
|
||||||
onChange: (key, newValue) => setState(updateState(state, key, newValue)),
|
onChange: (key, newValue) => setState(updateState(state, key, newValue)),
|
||||||
|
selectedValues,
|
||||||
reset,
|
reset,
|
||||||
someSelected,
|
someSelected,
|
||||||
toggleAll: {
|
toggleAll: {
|
||||||
|
|
|
@ -32,16 +32,16 @@ const endpoints = (build) => ({
|
||||||
...params
|
...params
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
providesTags: (res) =>
|
providesTags: (res) =>
|
||||||
res
|
res
|
||||||
? [...res.map((emoji) => ({type: "Emojis", id: emoji.id})), {type: "Emojis", id: "LIST"}]
|
? [...res.map((emoji) => ({ type: "Emojis", id: emoji.id })), { type: "Emojis", id: "LIST" }]
|
||||||
: [{type: "Emojis", id: "LIST"}]
|
: [{ type: "Emojis", id: "LIST" }]
|
||||||
}),
|
}),
|
||||||
getEmoji: build.query({
|
getEmoji: build.query({
|
||||||
query: (id) => ({
|
query: (id) => ({
|
||||||
url: `/api/v1/admin/custom_emojis/${id}`
|
url: `/api/v1/admin/custom_emojis/${id}`
|
||||||
}),
|
}),
|
||||||
providesTags: (res, error, id) => [{type: "Emojis", id}]
|
providesTags: (res, error, id) => [{ type: "Emojis", id }]
|
||||||
}),
|
}),
|
||||||
addEmoji: build.mutation({
|
addEmoji: build.mutation({
|
||||||
query: (form) => {
|
query: (form) => {
|
||||||
|
@ -49,16 +49,17 @@ const endpoints = (build) => ({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: `/api/v1/admin/custom_emojis`,
|
url: `/api/v1/admin/custom_emojis`,
|
||||||
asForm: true,
|
asForm: true,
|
||||||
body: form
|
body: form,
|
||||||
|
discardEmpty: true
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
invalidatesTags: (res) =>
|
invalidatesTags: (res) =>
|
||||||
res
|
res
|
||||||
? [{type: "Emojis", id: "LIST"}, {type: "Emojis", id: res.id}]
|
? [{ type: "Emojis", id: "LIST" }, { type: "Emojis", id: res.id }]
|
||||||
: [{type: "Emojis", id: "LIST"}]
|
: [{ type: "Emojis", id: "LIST" }]
|
||||||
}),
|
}),
|
||||||
editEmoji: build.mutation({
|
editEmoji: build.mutation({
|
||||||
query: ({id, ...patch}) => {
|
query: ({ id, ...patch }) => {
|
||||||
return {
|
return {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
url: `/api/v1/admin/custom_emojis/${id}`,
|
url: `/api/v1/admin/custom_emojis/${id}`,
|
||||||
|
@ -69,17 +70,17 @@ const endpoints = (build) => ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
invalidatesTags: (res) =>
|
invalidatesTags: (res) =>
|
||||||
res
|
res
|
||||||
? [{type: "Emojis", id: "LIST"}, {type: "Emojis", id: res.id}]
|
? [{ type: "Emojis", id: "LIST" }, { type: "Emojis", id: res.id }]
|
||||||
: [{type: "Emojis", id: "LIST"}]
|
: [{ type: "Emojis", id: "LIST" }]
|
||||||
}),
|
}),
|
||||||
deleteEmoji: build.mutation({
|
deleteEmoji: build.mutation({
|
||||||
query: (id) => ({
|
query: (id) => ({
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
url: `/api/v1/admin/custom_emojis/${id}`
|
url: `/api/v1/admin/custom_emojis/${id}`
|
||||||
}),
|
}),
|
||||||
invalidatesTags: (res, error, id) => [{type: "Emojis", id}]
|
invalidatesTags: (res, error, id) => [{ type: "Emojis", id }]
|
||||||
}),
|
}),
|
||||||
searchStatusForEmoji: build.mutation({
|
searchStatusForEmoji: build.mutation({
|
||||||
query: (url) => ({
|
query: (url) => ({
|
||||||
|
@ -88,7 +89,7 @@ const endpoints = (build) => ({
|
||||||
}),
|
}),
|
||||||
transformResponse: (res) => {
|
transformResponse: (res) => {
|
||||||
/* Parses search response, prioritizing a toot result,
|
/* Parses search response, prioritizing a toot result,
|
||||||
and returns referenced custom emoji
|
and returns referenced custom emoji
|
||||||
*/
|
*/
|
||||||
let type;
|
let type;
|
||||||
|
|
||||||
|
@ -112,7 +113,7 @@ const endpoints = (build) => ({
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
patchRemoteEmojis: build.mutation({
|
patchRemoteEmojis: build.mutation({
|
||||||
queryFn: ({action, domain, list, category}, api, _extraOpts, baseQuery) => {
|
queryFn: ({ action, domain, list, category }, api, _extraOpts, baseQuery) => {
|
||||||
const data = [];
|
const data = [];
|
||||||
const errors = [];
|
const errors = [];
|
||||||
|
|
||||||
|
@ -166,8 +167,8 @@ const endpoints = (build) => ({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
invalidatesTags: () => [{type: "Emojis", id: "LIST"}]
|
invalidatesTags: () => [{ type: "Emojis", id: "LIST" }]
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = base.injectEndpoints({endpoints});
|
module.exports = base.injectEndpoints({ endpoints });
|
|
@ -50,7 +50,7 @@ section {
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-bottom: 0.5rem;
|
margin-top: 0.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:only-child {
|
&:only-child {
|
||||||
|
@ -360,8 +360,25 @@ span.form-info {
|
||||||
.list {
|
.list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-height: 40rem;
|
|
||||||
overflow: auto;
|
&.scrolling {
|
||||||
|
max-height: 40rem;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header, .entry {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
border: 0.1rem solid transparent; /* for alignment with .entry border padding */
|
||||||
|
background: $gray2;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=checkbox] {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.entry {
|
.entry {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -372,17 +389,23 @@ span.form-info {
|
||||||
&:nth-child(even) {
|
&:nth-child(even) {
|
||||||
background: $settings-entry-alternate-bg;
|
background: $settings-entry-alternate-bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $settings-entry-hover-bg;
|
background: $settings-entry-hover-bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active, &:focus, &:hover {
|
&:active, &:focus, &:hover {
|
||||||
border-color: $fg-accent;
|
border-color: $fg-accent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkbox-list {
|
||||||
|
.header, .entry {
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.instance-list {
|
.instance-list {
|
||||||
.filter {
|
.filter {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -407,13 +430,16 @@ span.form-info {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-list {
|
.emoji-list {
|
||||||
background: $settings-entry-bg;
|
background: $settings-entry-bg;
|
||||||
|
|
||||||
.entry {
|
.entry {
|
||||||
padding: 0.5rem;
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
b {
|
||||||
|
padding-left: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
.emoji-group {
|
.emoji-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
@ -555,6 +581,7 @@ span.form-info {
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.emoji-detail {
|
.emoji-detail {
|
||||||
|
@ -612,8 +639,8 @@ span.form-info {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
|
||||||
& > span {
|
span {
|
||||||
margin-bottom: -1rem;
|
margin-bottom: -0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-buttons {
|
.action-buttons {
|
||||||
|
@ -621,27 +648,9 @@ span.form-info {
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-list {
|
.checkbox-list {
|
||||||
display: flex;
|
.entry {
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
& > * {
|
|
||||||
gap: 1rem;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
background: $gray2;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto auto 1fr;
|
grid-template-columns: auto auto 1fr;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: $settings-entry-hover-bg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.emoji {
|
.emoji {
|
||||||
|
@ -672,7 +681,7 @@ span.form-info {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button .fa-fw {
|
button .fa.with-text {
|
||||||
margin-left: -1.28571429em;
|
margin-left: -1.28571429em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue