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