mirror of
1
Fork 0

refactor parse-from-toot

This commit is contained in:
f0x 2023-01-11 19:52:52 +00:00
parent beb09aa827
commit 59413c3482
6 changed files with 100 additions and 89 deletions

View File

@ -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}
/>
</>
);

View File

@ -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} />

View File

@ -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

View File

@ -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: {

View File

@ -32,16 +32,16 @@ const endpoints = (build) => ({
...params
}
}),
providesTags: (res) =>
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) =>
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}`,
@ -69,17 +70,17 @@ const endpoints = (build) => ({
}
};
},
invalidatesTags: (res) =>
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 });

View File

@ -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;
@ -372,17 +389,23 @@ span.form-info {
&:nth-child(even) {
background: $settings-entry-alternate-bg;
}
&:hover {
background: $settings-entry-hover-bg;
}
&:active, &:focus, &:hover {
border-color: $fg-accent;
}
}
}
.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;
}