eslint --fix

This commit is contained in:
f0x 2024-11-06 15:55:00 +01:00
parent 8464f20370
commit 251286f2c7
94 changed files with 625 additions and 579 deletions

View File

@ -46,7 +46,7 @@ new PhotoswipeCaptionPlugin(lightbox, {
type: 'auto', type: 'auto',
captionContent(slide) { captionContent(slide) {
return slide.data.alt; return slide.data.alt;
} },
}); });
lightbox.addFilter('itemData', (item) => { lightbox.addFilter('itemData', (item) => {
@ -68,10 +68,10 @@ lightbox.addFilter('itemData', (item) => {
}, },
pause() { pause() {
el._player.pause(); el._player.pause();
} },
}, },
width: parseInt(el.dataset.pswpWidth), width: parseInt(el.dataset.pswpWidth),
height: parseInt(el.dataset.pswpHeight) height: parseInt(el.dataset.pswpHeight),
}; };
} }
return item; return item;
@ -169,12 +169,12 @@ Array.from(document.getElementsByClassName("plyr-video")).forEach((video) => {
}, 1); }, 1);
} }
lightbox.loadAndOpen(parseInt(video.dataset.pswpIndex), { lightbox.loadAndOpen(parseInt(video.dataset.pswpIndex), {
gallery: video.closest(".photoswipe-gallery") gallery: video.closest(".photoswipe-gallery"),
}); });
return false; return false;
} },
} },
}); });
player.elements.container.title = video.title; player.elements.container.title = video.title;

View File

@ -29,10 +29,10 @@ const prodCfg = {
transform: [ transform: [
["@browserify/uglifyify", { ["@browserify/uglifyify", {
global: true, global: true,
exts: ".js" exts: ".js",
}], }],
["@browserify/envify", { global: true }] ["@browserify/envify", { global: true }],
] ],
}; };
skulk({ skulk({
@ -42,8 +42,8 @@ skulk({
prodCfg: { prodCfg: {
servers: { servers: {
express: false, express: false,
livereload: false livereload: false,
} },
}, },
servers: { servers: {
express: { express: {
@ -54,6 +54,8 @@ skulk({
delete proxyRes.headers['content-security-policy']; delete proxyRes.headers['content-security-policy'];
}, },
}, },
assets: "/assets",
},
}, },
bundles: { bundles: {
frontend: { frontend: {
@ -64,8 +66,8 @@ skulk({
transform: [ transform: [
["babelify", { ["babelify", {
global: true, global: true,
ignore: [/node_modules\/(?!(photoswipe.*))/] ignore: [/node_modules\/(?!(photoswipe.*))/],
}] }],
], ],
}, },
settings: { settings: {
@ -75,7 +77,7 @@ skulk({
plugin: [ plugin: [
// Additional settings for TS are passed from tsconfig.json. // Additional settings for TS are passed from tsconfig.json.
// See: https://github.com/TypeStrong/tsify#tsconfigjson // See: https://github.com/TypeStrong/tsify#tsconfigjson
["tsify"] ["tsify"],
], ],
transform: [ transform: [
// tsify is called before babelify, so we're just babelifying // tsify is called before babelify, so we're just babelifying
@ -83,28 +85,28 @@ skulk({
["babelify", { ["babelify", {
global: true, global: true,
ignore: [/node_modules\/(?!(nanoid)|(wouter))/], ignore: [/node_modules\/(?!(nanoid)|(wouter))/],
}] }],
], ],
presets: [ presets: [
"react", "react",
["postcss", { ["postcss", {
output: "settings-style.css" output: "settings-style.css",
}] }],
] ],
}, },
cssThemes: { cssThemes: {
entryFiles: cssThemes, entryFiles: cssThemes,
outputFile: "_discard", outputFile: "_discard",
presets: [["postcss", { presets: [["postcss", {
output: "_split" output: "_split",
}]] }]],
}, },
css: { css: {
entryFiles: cssFiles, entryFiles: cssFiles,
outputFile: "_discard", outputFile: "_discard",
presets: [["postcss", { presets: [["postcss", {
output: "style.css" output: "style.css",
}]] }]],
} },
} },
}); });

View File

@ -19,7 +19,8 @@
import { useLogoutMutation, useVerifyCredentialsQuery } from "../../lib/query/oauth"; import { useLogoutMutation, useVerifyCredentialsQuery } from "../../lib/query/oauth";
import { store } from "../../redux/store"; import { store } from "../../redux/store";
import React, { ReactNode } from "react"; import type { ReactNode } from "react";
import React from "react";
import Login from "./login"; import Login from "./login";
import Loading from "../loading"; import Loading from "../loading";

View File

@ -29,7 +29,7 @@ import { TextInput } from "../form/inputs";
export default function Login({ }) { export default function Login({ }) {
const form = { const form = {
instance: useTextInput("instance", { instance: useTextInput("instance", {
defaultValue: window.location.origin defaultValue: window.location.origin,
}), }),
scopes: useValue("scopes", "user admin"), scopes: useValue("scopes", "user admin"),
}; };

View File

@ -17,10 +17,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React from "react"; import React, { memo, useDeferredValue, useCallback, useMemo } from "react";
import { memo, useDeferredValue, useCallback, useMemo } from "react"; import type { Checkable, ChecklistInputHook } from "../lib/form/types";
import { Checkable, ChecklistInputHook } from "../lib/form/types";
interface CheckListProps { interface CheckListProps {
field: ChecklistInputHook; field: ChecklistInputHook;
@ -75,7 +74,7 @@ const CheckListEntries = memo(
getExtraProps={getExtraProps} getExtraProps={getExtraProps}
/> />
)); ));
} },
); );
interface CheckListEntryProps { interface CheckListEntryProps {
@ -94,7 +93,7 @@ const CheckListEntry = memo(
function CheckListEntry({ entry, updateValue, getExtraProps, EntryComponent }: CheckListEntryProps) { function CheckListEntry({ entry, updateValue, getExtraProps, EntryComponent }: CheckListEntryProps) {
const onChange = useCallback( const onChange = useCallback(
(value) => updateValue(entry.key, value), (value) => updateValue(entry.key, value),
[updateValue, entry.key] [updateValue, entry.key],
); );
const extraProps = useMemo(() => getExtraProps?.(entry), [getExtraProps, entry]); const extraProps = useMemo(() => getExtraProps?.(entry), [getExtraProps, entry]);
@ -109,5 +108,5 @@ const CheckListEntry = memo(
<EntryComponent entry={entry} onChange={onChange} extraProps={extraProps} /> <EntryComponent entry={entry} onChange={onChange} extraProps={extraProps} />
</label> </label>
); );
} },
); );

View File

@ -17,9 +17,10 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { SerializedError } from "@reduxjs/toolkit"; import type { SerializedError } from "@reduxjs/toolkit";
import { FetchBaseQueryError } from "@reduxjs/toolkit/query"; import type { FetchBaseQueryError } from "@reduxjs/toolkit/query";
import React, { ReactNode } from "react"; import type { ReactNode } from "react";
import React from "react";
function ErrorFallback({ error, resetErrorBoundary }) { function ErrorFallback({ error, resetErrorBoundary }) {
return ( return (
@ -74,7 +75,7 @@ function Error({ error, reset }: ErrorProps) {
return null; return null;
} }
/* eslint-disable-next-line no-console */
console.error("caught error: ", error); console.error("caught error: ", error);
let message: ReactNode; let message: ReactNode;

View File

@ -21,7 +21,7 @@ import React from "react";
import { all } from "langs"; import { all } from "langs";
const asElements = all().map((l) => { const asElements = all().map((l) => {
let code = l["1"].toUpperCase(); const code = l["1"].toUpperCase();
let name = l.name; let name = l.name;
if (l.name != l.local) { if (l.name != l.local) {
name = `${name} - ${l.local}`; name = `${name} - ${l.local}`;

View File

@ -17,12 +17,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React, { ReactNode } from "react"; import type { ReactNode } from "react";
import React from "react";
import { useLocation } from "wouter"; import { useLocation } from "wouter";
import { Error } from "./error"; import { Error } from "./error";
import { SerializedError } from "@reduxjs/toolkit"; import type { SerializedError } from "@reduxjs/toolkit";
import { FetchBaseQueryError } from "@reduxjs/toolkit/query"; import type { FetchBaseQueryError } from "@reduxjs/toolkit/query";
import { Links } from "parse-link-header"; import type { Links } from "parse-link-header";
import Loading from "./loading"; import Loading from "./loading";
export interface PageableListProps<T> { export interface PageableListProps<T> {

View File

@ -19,14 +19,14 @@
import React from "react"; import React from "react";
import { useVerifyCredentialsQuery } from "../lib/query/oauth"; import { useVerifyCredentialsQuery } from "../lib/query/oauth";
import { MediaAttachment, Status as StatusType } from "../lib/types/status"; import type { MediaAttachment, Status as StatusType } from "../lib/types/status";
import sanitize from "sanitize-html"; import sanitize from "sanitize-html";
export function FakeStatus({ children }) { export function FakeStatus({ children }) {
const { data: account = { const { data: account = {
avatar: "/assets/default_avatars/GoToSocial_icon1.webp", avatar: "/assets/default_avatars/GoToSocial_icon1.webp",
display_name: "", display_name: "",
username: "" username: "",
} } = useVerifyCredentialsQuery(); } } = useVerifyCredentialsQuery();
return ( return (

View File

@ -19,7 +19,7 @@
import React from "react"; import React from "react";
import { useLocation } from "wouter"; import { useLocation } from "wouter";
import { AdminAccount } from "../lib/types/account"; import type { AdminAccount } from "../lib/types/account";
interface UsernameProps { interface UsernameProps {
account: AdminAccount; account: AdminAccount;
@ -32,7 +32,7 @@ export default function Username({ account, linkTo, backLocation, classNames }:
const [ _location, setLocation ] = useLocation(); const [ _location, setLocation ] = useLocation();
let className = "username-lozenge"; let className = "username-lozenge";
let isLocal = account.domain == null; const isLocal = account.domain == null;
if (account.suspended) { if (account.suspended) {
className += " suspended"; className += " suspended";
@ -46,7 +46,7 @@ export default function Username({ account, linkTo, backLocation, classNames }:
className = [ className, classNames ].flat().join(" "); className = [ className, classNames ].flat().join(" ");
} }
let icon = isLocal const icon = isLocal
? { fa: "fa-home", info: "Local user" } ? { fa: "fa-home", info: "Local user" }
: { fa: "fa-external-link-square", info: "Remote user" }; : { fa: "fa-external-link-square", info: "Remote user" };
@ -71,7 +71,7 @@ export default function Username({ account, linkTo, backLocation, classNames }:
// Store the back location in history so // Store the back location in history so
// the detail view can use it to return to // the detail view can use it to return to
// this page (including query parameters). // this page (including query parameters).
state: { backLocation: backLocation } state: { backLocation: backLocation },
}); });
}} }}
role="link" role="link"

View File

@ -26,7 +26,7 @@ import { PersistGate } from "redux-persist/integration/react";
import { store, persistor } from "./redux/store"; import { store, persistor } from "./redux/store";
import { Authorization } from "./components/authorization"; import { Authorization } from "./components/authorization";
import Loading from "./components/loading"; import Loading from "./components/loading";
import { Account } from "./lib/types/account"; import type { Account } from "./lib/types/account";
import { BaseUrlContext, RoleContext, InstanceDebugContext } from "./lib/navigation/util"; import { BaseUrlContext, RoleContext, InstanceDebugContext } from "./lib/navigation/util";
import { SidebarMenu } from "./lib/navigation/menu"; import { SidebarMenu } from "./lib/navigation/menu";
import { Redirect, Route, Router } from "wouter"; import { Redirect, Route, Router } from "wouter";

View File

@ -87,6 +87,6 @@ export default function useArrayInput(
} else { } else {
return []; return [];
} }
} },
}; };
} }

View File

@ -27,7 +27,7 @@ import type {
const _default = false; const _default = false;
export default function useBoolInput( export default function useBoolInput(
{ name, Name }: CreateHookNames, { name, Name }: CreateHookNames,
{ initialValue = _default }: HookOpts<boolean> { initialValue = _default }: HookOpts<boolean>,
): BoolFormInputHook { ): BoolFormInputHook {
const [value, setValue] = useState(initialValue); const [value, setValue] = useState(initialValue);
@ -45,8 +45,8 @@ export default function useBoolInput(
reset, reset,
{ {
[name]: value, [name]: value,
[`set${Name}`]: setValue [`set${Name}`]: setValue,
} },
], { ], {
name, name,
Name: "", Name: "",
@ -55,6 +55,6 @@ export default function useBoolInput(
value, value,
setter: setValue, setter: setValue,
hasChanged: () => value != initialValue, hasChanged: () => value != initialValue,
_default _default,
}); });
} }

View File

@ -38,20 +38,20 @@ import {
const _default: { [k: string]: Checkable } = {}; const _default: { [k: string]: Checkable } = {};
export default function useCheckListInput( export default function useCheckListInput(
/* eslint-disable no-unused-vars */
{ name, Name }: CreateHookNames, { name, Name }: CreateHookNames,
{ {
entries = [], entries = [],
uniqueKey = "key", uniqueKey = "key",
initialValue = false, initialValue = false,
}: HookOpts<boolean> }: HookOpts<boolean>,
): ChecklistInputHook { ): ChecklistInputHook {
const [state, dispatch] = useChecklistReducer(entries, uniqueKey, initialValue); const [state, dispatch] = useChecklistReducer(entries, uniqueKey, initialValue);
const toggleAllRef = useRef<any>(null); const toggleAllRef = useRef<any>(null);
useEffect(() => { useEffect(() => {
if (toggleAllRef.current != null) { if (toggleAllRef.current != null) {
let some = state.selectedEntries.size > 0; const some = state.selectedEntries.size > 0;
let all = false; let all = false;
if (some) { if (some) {
all = state.selectedEntries.size == Object.values(state.entries).length; all = state.selectedEntries.size == Object.values(state.entries).length;
@ -64,18 +64,24 @@ export default function useCheckListInput(
}, [state.selectedEntries]); }, [state.selectedEntries]);
const reset = useCallback( const reset = useCallback(
() => dispatch(actions.updateAll(initialValue)), () => {
[initialValue, dispatch] dispatch(actions.updateAll(initialValue));
},
[initialValue, dispatch],
); );
const onChange = useCallback( const onChange = useCallback(
(key: string, value: Checkable) => dispatch(actions.update({ key, value })), (key: string, value: Checkable) => {
[dispatch] dispatch(actions.update({ key, value }));
},
[dispatch],
); );
const updateMultiple = useCallback( const updateMultiple = useCallback(
(entries: [key: string, value: Partial<Checkable>][]) => dispatch(actions.updateMultiple(entries)), (entries: [key: string, value: Partial<Checkable>][]) => {
[dispatch] dispatch(actions.updateMultiple(entries));
},
[dispatch],
); );
return useMemo(() => { return useMemo(() => {
@ -89,14 +95,14 @@ export default function useCheckListInput(
function selectedValues() { function selectedValues() {
return Array.from((state.selectedEntries)).map((key) => ({ return Array.from((state.selectedEntries)).map((key) => ({
...state.entries[key] // returned as new object, because reducer state is immutable ...state.entries[key], // returned as new object, because reducer state is immutable
})); }));
} }
return Object.assign([ return Object.assign([
state, state,
reset, reset,
{ name } { name },
], { ], {
_default, _default,
hasChanged: () => true, hasChanged: () => true,
@ -110,8 +116,8 @@ export default function useCheckListInput(
updateMultiple, updateMultiple,
toggleAll: { toggleAll: {
ref: toggleAllRef, ref: toggleAllRef,
onChange: toggleAll onChange: toggleAll,
} },
}); });
}, [state, reset, name, onChange, updateMultiple, dispatch]); }, [state, reset, name, onChange, updateMultiple, dispatch]);
} }

View File

@ -20,7 +20,7 @@
import { useState } from "react"; import { useState } from "react";
import { useComboboxState } from "ariakit/combobox"; import { useComboboxState } from "ariakit/combobox";
import { import type {
ComboboxFormInputHook, ComboboxFormInputHook,
CreateHookNames, CreateHookNames,
HookOpts, HookOpts,
@ -29,14 +29,14 @@ import {
const _default = ""; const _default = "";
export default function useComboBoxInput( export default function useComboBoxInput(
{ name, Name }: CreateHookNames, { name, Name }: CreateHookNames,
{ initialValue = _default }: HookOpts<string> { initialValue = _default }: HookOpts<string>,
): ComboboxFormInputHook { ): ComboboxFormInputHook {
const [isNew, setIsNew] = useState(false); const [isNew, setIsNew] = useState(false);
const state = useComboboxState({ const state = useComboboxState({
defaultValue: initialValue, defaultValue: initialValue,
gutter: 0, gutter: 0,
sameWidth: true sameWidth: true,
}); });
function reset() { function reset() {
@ -50,18 +50,20 @@ export default function useComboBoxInput(
[name]: state.value, [name]: state.value,
name, name,
[`${name}IsNew`]: isNew, [`${name}IsNew`]: isNew,
[`set${Name}IsNew`]: setIsNew [`set${Name}IsNew`]: setIsNew,
} },
], { ], {
reset, reset,
name, name,
Name: "", // Will be set by inputHook function. Name: "", // Will be set by inputHook function.
state, state,
value: state.value, value: state.value,
setter: (val: string) => state.setValue(val), setter: (val: string) => {
state.setValue(val);
},
hasChanged: () => state.value != initialValue, hasChanged: () => state.value != initialValue,
isNew, isNew,
setIsNew, setIsNew,
_default _default,
}); });
} }

View File

@ -80,6 +80,6 @@ export default function useFieldArrayInput(
} else { } else {
return []; return [];
} }
} },
}; };
} }

View File

@ -17,9 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React from "react"; import React, { useState } from "react";
import { useState } from "react";
import prettierBytes from "prettier-bytes"; import prettierBytes from "prettier-bytes";
import type { import type {
@ -35,8 +34,8 @@ export default function useFileInput(
{ {
withPreview, withPreview,
maxSize, maxSize,
initialInfo = "no file selected" initialInfo = "no file selected",
}: HookOpts<File> }: HookOpts<File>,
): FileFormInputHook { ): FileFormInputHook {
const [file, setFile] = useState<File>(); const [file, setFile] = useState<File>();
const [imageURL, setImageURL] = useState<string>(); const [imageURL, setImageURL] = useState<string>();
@ -58,7 +57,7 @@ export default function useFileInput(
return; return;
} }
let file = files[0]; const file = files[0];
setFile(file); setFile(file);
if (imageURL) { if (imageURL) {
@ -76,7 +75,7 @@ export default function useFileInput(
<ErrorC <ErrorC
error={new Error(`file size ${sizePrettier} is larger than max size ${maxSizePrettier}`)} error={new Error(`file size ${sizePrettier} is larger than max size ${maxSizePrettier}`)}
reset={(reset)} reset={(reset)}
/> />,
); );
} else { } else {
setInfo(<>{file.name} ({sizePrettier})</>); setInfo(<>{file.name} ({sizePrettier})</>);
@ -100,7 +99,7 @@ export default function useFileInput(
[name]: file, [name]: file,
[`${name}URL`]: imageURL, [`${name}URL`]: imageURL,
[`${name}Info`]: infoComponent, [`${name}Info`]: infoComponent,
} },
], { ], {
onChange, onChange,
reset, reset,

View File

@ -17,14 +17,12 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/* eslint-disable no-unused-vars */
import React from "react"; import React from "react";
import { Error } from "../../components/error"; import { Error } from "../../components/error";
import Loading from "../../components/loading"; import Loading from "../../components/loading";
import { NoArg } from "../types/query"; import { NoArg } from "../types/query";
import { FormWithDataQuery } from "./types"; import type { FormWithDataQuery } from "./types";
export interface FormWithDataProps { export interface FormWithDataProps {
dataQuery: FormWithDataQuery, dataQuery: FormWithDataQuery,

View File

@ -17,13 +17,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { FormInputHook, HookedForm } from "./types"; import type { FormInputHook, HookedForm } from "./types";
export default function getFormMutations( export default function getFormMutations(
form: HookedForm, form: HookedForm,
{ changedOnly }: { changedOnly: boolean }, { changedOnly }: { changedOnly: boolean },
): { ): {
updatedFields: FormInputHook<any>[]; updatedFields: FormInputHook[];
mutationData: { mutationData: {
[k: string]: any; [k: string]: any;
}; };

View File

@ -18,7 +18,7 @@
*/ */
import { useState } from "react"; import { useState } from "react";
import { CreateHookNames, HookOpts, RadioFormInputHook } from "./types"; import type { CreateHookNames, HookOpts, RadioFormInputHook } from "./types";
const _default = ""; const _default = "";
export default function useRadioInput( export default function useRadioInput(
@ -26,7 +26,7 @@ export default function useRadioInput(
{ {
initialValue = _default, initialValue = _default,
options = {}, options = {},
}: HookOpts<string> }: HookOpts<string>,
): RadioFormInputHook { ): RadioFormInputHook {
const [value, setValue] = useState(initialValue); const [value, setValue] = useState(initialValue);
@ -44,8 +44,8 @@ export default function useRadioInput(
reset, reset,
{ {
[name]: value, [name]: value,
[`set${Name}`]: setValue [`set${Name}`]: setValue,
} },
], { ], {
onChange, onChange,
reset, reset,
@ -55,6 +55,6 @@ export default function useRadioInput(
setter: setValue, setter: setValue,
options, options,
hasChanged: () => value != initialValue, hasChanged: () => value != initialValue,
_default _default,
}); });
} }

View File

@ -60,7 +60,7 @@ interface UseFormSubmitOptions {
export default function useFormSubmit( export default function useFormSubmit(
form: HookedForm, form: HookedForm,
mutationQuery: readonly [MutationTrigger<any>, UseMutationStateResult<any, any>], mutationQuery: readonly [MutationTrigger<any>, UseMutationStateResult<any, any>],
opts: UseFormSubmitOptions = { changedOnly: true } opts: UseFormSubmitOptions = { changedOnly: true },
): [ FormSubmitFunction, FormSubmitResult ] { ): [ FormSubmitFunction, FormSubmitResult ] {
if (!Array.isArray(mutationQuery)) { if (!Array.isArray(mutationQuery)) {
throw "useFormSubmit: mutationQuery was not an Array. Is a valid useMutation RTK Query provided?"; throw "useFormSubmit: mutationQuery was not an Array. Is a valid useMutation RTK Query provided?";
@ -90,7 +90,7 @@ export default function useFormSubmit(
// what at this point. If it's an empty string, fall back to undefined. // what at this point. If it's an empty string, fall back to undefined.
// //
// See: https://developer.mozilla.org/en-US/docs/Web/API/SubmitEvent/submitter // See: https://developer.mozilla.org/en-US/docs/Web/API/SubmitEvent/submitter
action = (e.nativeEvent.submitter as Object as { name: string }).name || undefined; action = (e.nativeEvent.submitter as object as { name: string }).name || undefined;
} else { } else {
// No submitter defined. Fall back // No submitter defined. Fall back
// to just use the FormSubmitEvent. // to just use the FormSubmitEvent.
@ -125,7 +125,7 @@ export default function useFormSubmit(
onFinish(res); onFinish(res);
} }
} catch (e) { } catch (e) {
// eslint-disable-next-line no-console
console.error(`caught error running mutation: ${e}`); console.error(`caught error running mutation: ${e}`);
} }
}; };
@ -134,7 +134,7 @@ export default function useFormSubmit(
submitForm, submitForm,
{ {
...mutationResult, ...mutationResult,
action: usedAction.current action: usedAction.current,
} },
]; ];
} }

View File

@ -17,7 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React, { import type React from "react";
import {
useState, useState,
useRef, useRef,
useTransition, useTransition,
@ -41,7 +42,7 @@ export default function useTextInput(
showValidation = true, showValidation = true,
initValidation, initValidation,
nosubmit = false, nosubmit = false,
}: HookOpts<string> }: HookOpts<string>,
): TextFormInputHook { ): TextFormInputHook {
const [text, setText] = useState(initialValue); const [text, setText] = useState(initialValue);
const textRef = useRef<HTMLInputElement>(null); const textRef = useRef<HTMLInputElement>(null);
@ -86,7 +87,7 @@ export default function useTextInput(
[`${name}Ref`]: textRef, [`${name}Ref`]: textRef,
[`set${Name}`]: setText, [`set${Name}`]: setText,
[`${name}Valid`]: valid, [`${name}Valid`]: valid,
} },
], { ], {
onChange, onChange,
reset, reset,
@ -97,8 +98,10 @@ export default function useTextInput(
ref: textRef, ref: textRef,
setter: setText, setter: setText,
valid, valid,
validate: () => setValidation(validator ? validator(text): ""), validate: () => {
setValidation(validator ? validator(text): "");
},
hasChanged: () => text != initialValue, hasChanged: () => text != initialValue,
_default _default,
}); });
} }

View File

@ -17,12 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/* eslint-disable no-unused-vars */ import type { ComboboxState } from "ariakit";
import type React, {
import { ComboboxState } from "ariakit";
import React from "react";
import {
ChangeEventHandler, ChangeEventHandler,
Dispatch, Dispatch,
RefObject, RefObject,
@ -30,6 +26,7 @@ import {
SyntheticEvent, SyntheticEvent,
} from "react"; } from "react";
export interface CreateHookNames { export interface CreateHookNames {
name: string; name: string;
Name: string; Name: string;

View File

@ -17,7 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React, { Component, ReactNode } from "react"; import type { ReactNode } from "react";
import React, { Component } from "react";
interface ErrorBoundaryProps { interface ErrorBoundaryProps {
@ -48,7 +49,7 @@ class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
componentDidCatch(_e, info) { componentDidCatch(_e, info) {
this.setState({ this.setState({
...this.state, ...this.state,
componentStack: info.componentStack componentStack: info.componentStack,
}); });
} }
@ -83,7 +84,7 @@ function ErrorFallback({ error, componentStack, resetErrorBoundary }) {
{componentStack && [ {componentStack && [
"\n\nComponent trace:", "\n\nComponent trace:",
componentStack componentStack,
]} ]}
{["\n\nError trace: ", error.stack]} {["\n\nError trace: ", error.stack]}
</pre> </pre>

View File

@ -17,7 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React, { PropsWithChildren } from "react"; import type { PropsWithChildren } from "react";
import React from "react";
import { Link, useRoute } from "wouter"; import { Link, useRoute } from "wouter";
import { import {
BaseUrlContext, BaseUrlContext,

View File

@ -26,9 +26,9 @@ const extended = gtsApi.injectEndpoints({
method: "POST", method: "POST",
url: `/api/v1/admin/media_cleanup`, url: `/api/v1/admin/media_cleanup`,
params: { params: {
remote_cache_days: days remote_cache_days: days,
} },
}) }),
}), }),
instanceKeysExpire: build.mutation({ instanceKeysExpire: build.mutation({
@ -36,9 +36,9 @@ const extended = gtsApi.injectEndpoints({
method: "POST", method: "POST",
url: `/api/v1/admin/domain_keys_expire`, url: `/api/v1/admin/domain_keys_expire`,
params: { params: {
domain: domain domain: domain,
} },
}) }),
}), }),
sendTestEmail: build.mutation<any, { email: string, message?: string }>({ sendTestEmail: build.mutation<any, { email: string, message?: string }>({
@ -46,7 +46,7 @@ const extended = gtsApi.injectEndpoints({
method: "POST", method: "POST",
url: `/api/v1/admin/email/test`, url: `/api/v1/admin/email/test`,
params: params, params: params,
}) }),
}), }),
}), }),
}); });

View File

@ -18,8 +18,8 @@
*/ */
import { gtsApi } from "../../gts-api"; import { gtsApi } from "../../gts-api";
import { FetchBaseQueryError } from "@reduxjs/toolkit/query"; import type { FetchBaseQueryError } from "@reduxjs/toolkit/query";
import { RootState } from "../../../../redux/store"; import type { RootState } from "../../../../redux/store";
import type { CustomEmoji, EmojisFromItem, ListEmojiParams } from "../../../types/custom-emoji"; import type { CustomEmoji, EmojisFromItem, ListEmojiParams } from "../../../types/custom-emoji";
@ -77,39 +77,39 @@ const extended = gtsApi.injectEndpoints({
url: "/api/v1/admin/custom_emojis", url: "/api/v1/admin/custom_emojis",
params: { params: {
limit: 0, limit: 0,
...params ...params,
} },
}), }),
providesTags: (res, _error, _arg) => providesTags: (res, _error, _arg) =>
res res
? [ ? [
...res.map((emoji) => ({ type: "Emoji" as const, id: emoji.id })), ...res.map((emoji) => ({ type: "Emoji" as const, id: emoji.id })),
{ type: "Emoji", id: "LIST" } { type: "Emoji", id: "LIST" },
] ]
: [{ type: "Emoji", id: "LIST" }] : [{ type: "Emoji", id: "LIST" }],
}), }),
getEmoji: build.query<CustomEmoji, string>({ getEmoji: build.query<CustomEmoji, string>({
query: (id) => ({ query: (id) => ({
url: `/api/v1/admin/custom_emojis/${id}` url: `/api/v1/admin/custom_emojis/${id}`,
}), }),
providesTags: (_res, _error, id) => [{ type: "Emoji", id }] providesTags: (_res, _error, id) => [{ type: "Emoji", id }],
}), }),
addEmoji: build.mutation<CustomEmoji, Object>({ addEmoji: build.mutation<CustomEmoji, object>({
query: (form) => { query: (form) => {
return { return {
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 discardEmpty: true,
}; };
}, },
invalidatesTags: (res) => invalidatesTags: (res) =>
res res
? [{ type: "Emoji", id: "LIST" }, { type: "Emoji", id: res.id }] ? [{ type: "Emoji", id: "LIST" }, { type: "Emoji", id: res.id }]
: [{ type: "Emoji", id: "LIST" }] : [{ type: "Emoji", id: "LIST" }],
}), }),
editEmoji: build.mutation<CustomEmoji, any>({ editEmoji: build.mutation<CustomEmoji, any>({
@ -120,22 +120,22 @@ const extended = gtsApi.injectEndpoints({
asForm: true, asForm: true,
body: { body: {
type: "modify", type: "modify",
...patch ...patch,
} },
}; };
}, },
invalidatesTags: (res) => invalidatesTags: (res) =>
res res
? [{ type: "Emoji", id: "LIST" }, { type: "Emoji", id: res.id }] ? [{ type: "Emoji", id: "LIST" }, { type: "Emoji", id: res.id }]
: [{ type: "Emoji", id: "LIST" }] : [{ type: "Emoji", id: "LIST" }],
}), }),
deleteEmoji: build.mutation<any, string>({ deleteEmoji: build.mutation<any, string>({
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: "Emoji", id }] invalidatesTags: (_res, _error, id) => [{ type: "Emoji", id }],
}), }),
searchItemForEmoji: build.mutation<EmojisFromItem, string>({ searchItemForEmoji: build.mutation<EmojisFromItem, string>({
@ -145,10 +145,10 @@ const extended = gtsApi.injectEndpoints({
// First search for given url. // First search for given url.
const searchRes = await fetchWithBQ({ const searchRes = await fetchWithBQ({
url: `/api/v2/search?q=${encodeURIComponent(url)}&resolve=true&limit=1` url: `/api/v2/search?q=${encodeURIComponent(url)}&resolve=true&limit=1`,
}); });
if (searchRes.error) { if (searchRes.error) {
return { error: searchRes.error as FetchBaseQueryError }; return { error: searchRes.error };
} }
// Parse initial results of search. // Parse initial results of search.
@ -178,8 +178,8 @@ const extended = gtsApi.injectEndpoints({
url: `/api/v1/admin/custom_emojis`, url: `/api/v1/admin/custom_emojis`,
params: { params: {
filter: `domain:${domain},shortcode:${emoji.shortcode}`, filter: `domain:${domain},shortcode:${emoji.shortcode}`,
limit: 1 limit: 1,
} },
}); });
if (emojiRes.error) { if (emojiRes.error) {
@ -191,7 +191,7 @@ const extended = gtsApi.injectEndpoints({
// Got it! // Got it!
return emojiRes.data as CustomEmoji; return emojiRes.data as CustomEmoji;
}) }),
) )
).flatMap((emoji) => { ).flatMap((emoji) => {
// Remove any nulls. // Remove any nulls.
@ -205,7 +205,7 @@ const extended = gtsApi.injectEndpoints({
status: 400, status: 400,
statusText: 'Bad Request', statusText: 'Bad Request',
data: { data: {
error: `One or more errors fetching custom emojis: [${errData}]` error: `One or more errors fetching custom emojis: [${errData}]`,
}, },
}, },
}; };
@ -218,9 +218,9 @@ const extended = gtsApi.injectEndpoints({
type, type,
domain, domain,
list: withIDs, list: withIDs,
} },
}; };
} },
}), }),
patchRemoteEmojis: build.mutation({ patchRemoteEmojis: build.mutation({
@ -231,7 +231,7 @@ const extended = gtsApi.injectEndpoints({
// Map function to get a promise // Map function to get a promise
// of an emoji (or null). // of an emoji (or null).
const copyEmoji = async(emoji: CustomEmoji) => { const copyEmoji = async(emoji: CustomEmoji) => {
let body: { const body: {
type: string; type: string;
shortcode?: string; shortcode?: string;
category?: string; category?: string;
@ -285,7 +285,7 @@ const extended = gtsApi.injectEndpoints({
status: 400, status: 400,
statusText: 'Bad Request', statusText: 'Bad Request',
data: { data: {
error: `One or more errors patching custom emojis: [${errData}]` error: `One or more errors patching custom emojis: [${errData}]`,
}, },
}, },
}; };
@ -293,9 +293,9 @@ const extended = gtsApi.injectEndpoints({
return { data }; return { data };
}, },
invalidatesTags: () => [{ type: "Emoji", id: "LIST" }] invalidatesTags: () => [{ type: "Emoji", id: "LIST" }],
}) }),
}) }),
}); });
/** /**

View File

@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { ApURLResponse } from "../../../types/debug"; import type { ApURLResponse } from "../../../types/debug";
import { gtsApi } from "../../gts-api"; import { gtsApi } from "../../gts-api";
const extended = gtsApi.injectEndpoints({ const extended = gtsApi.injectEndpoints({
@ -32,13 +32,13 @@ const extended = gtsApi.injectEndpoints({
return { return {
url: `/api/v1/admin/debug/apurl?${urlParam.toString()}`, url: `/api/v1/admin/debug/apurl?${urlParam.toString()}`,
}; };
} },
}), }),
ClearCaches: build.mutation<{}, void>({ ClearCaches: build.mutation<{}, void>({
query: () => ({ query: () => ({
method: "POST", method: "POST",
url: `/api/v1/admin/debug/caches/clear` url: `/api/v1/admin/debug/caches/clear`,
}) }),
}), }),
}), }),
}); });

View File

@ -21,9 +21,9 @@ import fileDownload from "js-file-download";
import { unparse as csvUnparse } from "papaparse"; import { unparse as csvUnparse } from "papaparse";
import { gtsApi } from "../../gts-api"; import { gtsApi } from "../../gts-api";
import { RootState } from "../../../../redux/store"; import type { RootState } from "../../../../redux/store";
import { FetchBaseQueryError } from "@reduxjs/toolkit/query"; import type { FetchBaseQueryError } from "@reduxjs/toolkit/query";
import { DomainPerm, ExportDomainPermsParams } from "../../../types/domain-permission"; import type { DomainPerm, ExportDomainPermsParams } from "../../../types/domain-permission";
interface _exportProcess { interface _exportProcess {
transformEntry: (_entry: DomainPerm) => any; transformEntry: (_entry: DomainPerm) => any;
@ -45,11 +45,11 @@ function exportProcess(formData: ExportDomainPermsParams): _exportProcess {
transformEntry: (entry) => ({ transformEntry: (entry) => ({
domain: entry.domain, domain: entry.domain,
public_comment: entry.public_comment, public_comment: entry.public_comment,
obfuscate: entry.obfuscate obfuscate: entry.obfuscate,
}), }),
stringify: (list) => JSON.stringify(list), stringify: (list) => JSON.stringify(list),
extension: ".json", extension: ".json",
mime: "application/json" mime: "application/json",
}; };
} }
@ -61,7 +61,7 @@ function exportProcess(formData: ExportDomainPermsParams): _exportProcess {
false, // reject_media false, // reject_media
false, // reject_reports false, // reject_reports
entry.public_comment ?? "", // public_comment entry.public_comment ?? "", // public_comment
entry.obfuscate ?? false // obfuscate entry.obfuscate ?? false, // obfuscate
], ],
stringify: (list) => csvUnparse({ stringify: (list) => csvUnparse({
fields: [ fields: [
@ -72,10 +72,10 @@ function exportProcess(formData: ExportDomainPermsParams): _exportProcess {
"#public_comment", "#public_comment",
"#obfuscate", "#obfuscate",
], ],
data: list data: list,
}), }),
extension: ".csv", extension: ".csv",
mime: "text/csv" mime: "text/csv",
}; };
} }
@ -84,7 +84,7 @@ function exportProcess(formData: ExportDomainPermsParams): _exportProcess {
transformEntry: (entry) => entry.domain, transformEntry: (entry) => entry.domain,
stringify: (list) => list.join("\n"), stringify: (list) => list.join("\n"),
extension: ".txt", extension: ".txt",
mime: "text/plain" mime: "text/plain",
}; };
} }
@ -98,7 +98,7 @@ const extended = gtsApi.injectEndpoints({
// we want the untransformed array version. // we want the untransformed array version.
const permsRes = await fetchWithBQ({ url: `/api/v1/admin/domain_${formData.permType}s` }); const permsRes = await fetchWithBQ({ url: `/api/v1/admin/domain_${formData.permType}s` });
if (permsRes.error) { if (permsRes.error) {
return { error: permsRes.error as FetchBaseQueryError }; return { error: permsRes.error };
} }
// Process perms into desired export format. // Process perms into desired export format.
@ -130,16 +130,16 @@ const extended = gtsApi.injectEndpoints({
fileDownload( fileDownload(
exportAsString, exportAsString,
filename + process.extension, filename + process.extension,
process.mime process.mime,
); );
// js-file-download handles the // js-file-download handles the
// nitty gritty for us, so we can // nitty gritty for us, so we can
// just return null data. // just return null data.
return { data: null }; return { data: null };
} },
}),
}), }),
})
}); });
/** /**

View File

@ -26,14 +26,14 @@ const extended = gtsApi.injectEndpoints({
endpoints: (build) => ({ endpoints: (build) => ({
domainBlocks: build.query<MappedDomainPerms, void>({ domainBlocks: build.query<MappedDomainPerms, void>({
query: () => ({ query: () => ({
url: `/api/v1/admin/domain_blocks` url: `/api/v1/admin/domain_blocks`,
}), }),
transformResponse: listToKeyedObject<DomainPerm>("domain"), transformResponse: listToKeyedObject<DomainPerm>("domain"),
}), }),
domainAllows: build.query<MappedDomainPerms, void>({ domainAllows: build.query<MappedDomainPerms, void>({
query: () => ({ query: () => ({
url: `/api/v1/admin/domain_allows` url: `/api/v1/admin/domain_allows`,
}), }),
transformResponse: listToKeyedObject<DomainPerm>("domain"), transformResponse: listToKeyedObject<DomainPerm>("domain"),
}), }),

View File

@ -36,7 +36,7 @@ import { listToKeyedObject } from "../../transforms";
* @returns * @returns
*/ */
function importEntriesProcessor(formData: ImportDomainPermsParams): (_entry: DomainPerm) => DomainPerm { function importEntriesProcessor(formData: ImportDomainPermsParams): (_entry: DomainPerm) => DomainPerm {
let processingFuncs: { (_entry: DomainPerm): void; }[] = []; const processingFuncs: { (_entry: DomainPerm): void; }[] = [];
// Override each obfuscate entry if necessary. // Override each obfuscate entry if necessary.
if (formData.obfuscate !== undefined) { if (formData.obfuscate !== undefined) {
@ -49,7 +49,7 @@ function importEntriesProcessor(formData: ImportDomainPermsParams): (_entry: Dom
// Check whether we need to append or replace // Check whether we need to append or replace
// private_comment and public_comment. // private_comment and public_comment.
["private_comment","public_comment"].forEach((commentType) => { ["private_comment","public_comment"].forEach((commentType) => {
let text = formData.commentType?.trim(); const text = formData.commentType?.trim();
if (!text) { if (!text) {
return; return;
} }
@ -78,7 +78,9 @@ function importEntriesProcessor(formData: ImportDomainPermsParams): (_entry: Dom
return function process(entry) { return function process(entry) {
// Call all the assembled processing functions. // Call all the assembled processing functions.
processingFuncs.forEach((f) => f(entry)); processingFuncs.forEach((f) => {
f(entry);
});
// Unset all internal processing keys // Unset all internal processing keys
// and any undefined keys on this entry. // and any undefined keys on this entry.
@ -111,7 +113,7 @@ const extended = gtsApi.injectEndpoints({
[JSON.stringify(domains)], [JSON.stringify(domains)],
{ type: "application/json" }, { type: "application/json" },
), ),
} },
}; };
}, },
transformResponse: listToKeyedObject<DomainPerm>("domain"), transformResponse: listToKeyedObject<DomainPerm>("domain"),
@ -125,8 +127,8 @@ const extended = gtsApi.injectEndpoints({
formData.permType.slice(1); formData.permType.slice(1);
return `domain${permType}s`; return `domain${permType}s`;
}), }),
}) }),
}) }),
}); });
/** /**

View File

@ -17,9 +17,10 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import type {
ParseConfig as CSVParseConfig} from "papaparse";
import { import {
ParseConfig as CSVParseConfig, parse as csvParse,
parse as csvParse
} from "papaparse"; } from "papaparse";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
@ -75,7 +76,7 @@ function parseDomainList(list: string): DomainPerm[] {
"reject_reports": true, "reject_reports": true,
"public_comment": false, "public_comment": false,
"obfuscate": true, "obfuscate": true,
} },
}; };
const { data, errors } = csvParse(list, csvParseCfg); const { data, errors } = csvParse(list, csvParseCfg);
@ -119,7 +120,7 @@ function parseDomainList(list: string): DomainPerm[] {
} }
function deduplicateDomainList(list: DomainPerm[]): DomainPerm[] { function deduplicateDomainList(list: DomainPerm[]): DomainPerm[] {
let domains = new Set(); const domains = new Set();
return list.filter((entry) => { return list.filter((entry) => {
if (domains.has(entry.domain)) { if (domains.has(entry.domain)) {
return false; return false;
@ -168,9 +169,9 @@ const extended = gtsApi.injectEndpoints({
}); });
return { data: validated }; return { data: validated };
} },
}) }),
}) }),
}); });
/** /**

View File

@ -26,7 +26,7 @@ import {
import { listToKeyedObject } from "../../transforms"; import { listToKeyedObject } from "../../transforms";
import type { import type {
DomainPerm, DomainPerm,
MappedDomainPerms MappedDomainPerms,
} from "../../../types/domain-permission"; } from "../../../types/domain-permission";
const extended = gtsApi.injectEndpoints({ const extended = gtsApi.injectEndpoints({
@ -37,7 +37,7 @@ const extended = gtsApi.injectEndpoints({
url: `/api/v1/admin/domain_blocks`, url: `/api/v1/admin/domain_blocks`,
asForm: true, asForm: true,
body: formData, body: formData,
discardEmpty: true discardEmpty: true,
}), }),
transformResponse: listToKeyedObject<DomainPerm>("domain"), transformResponse: listToKeyedObject<DomainPerm>("domain"),
...replaceCacheOnMutation("domainBlocks"), ...replaceCacheOnMutation("domainBlocks"),
@ -49,10 +49,10 @@ const extended = gtsApi.injectEndpoints({
url: `/api/v1/admin/domain_allows`, url: `/api/v1/admin/domain_allows`,
asForm: true, asForm: true,
body: formData, body: formData,
discardEmpty: true discardEmpty: true,
}), }),
transformResponse: listToKeyedObject<DomainPerm>("domain"), transformResponse: listToKeyedObject<DomainPerm>("domain"),
...replaceCacheOnMutation("domainAllows") ...replaceCacheOnMutation("domainAllows"),
}), }),
removeDomainBlock: build.mutation<DomainPerm, string>({ removeDomainBlock: build.mutation<DomainPerm, string>({
@ -63,8 +63,8 @@ const extended = gtsApi.injectEndpoints({
...removeFromCacheOnMutation("domainBlocks", { ...removeFromCacheOnMutation("domainBlocks", {
key: (_draft, newData) => { key: (_draft, newData) => {
return newData.domain; return newData.domain;
} },
}) }),
}), }),
removeDomainAllow: build.mutation<DomainPerm, string>({ removeDomainAllow: build.mutation<DomainPerm, string>({
@ -75,8 +75,8 @@ const extended = gtsApi.injectEndpoints({
...removeFromCacheOnMutation("domainAllows", { ...removeFromCacheOnMutation("domainAllows", {
key: (_draft, newData) => { key: (_draft, newData) => {
return newData.domain; return newData.domain;
} },
}) }),
}), }),
}), }),
}); });
@ -105,5 +105,5 @@ export {
useAddDomainBlockMutation, useAddDomainBlockMutation,
useAddDomainAllowMutation, useAddDomainAllowMutation,
useRemoveDomainBlockMutation, useRemoveDomainBlockMutation,
useRemoveDomainAllowMutation useRemoveDomainAllowMutation,
}; };

View File

@ -18,7 +18,7 @@
*/ */
import { gtsApi } from "../../gts-api"; import { gtsApi } from "../../gts-api";
import { HeaderPermission } from "../../../types/http-header-permissions"; import type { HeaderPermission } from "../../../types/http-header-permissions";
const extended = gtsApi.injectEndpoints({ const extended = gtsApi.injectEndpoints({
endpoints: (build) => ({ endpoints: (build) => ({
@ -27,7 +27,7 @@ const extended = gtsApi.injectEndpoints({
getHeaderAllows: build.query<HeaderPermission[], void>({ getHeaderAllows: build.query<HeaderPermission[], void>({
query: () => ({ query: () => ({
url: `/api/v1/admin/header_allows` url: `/api/v1/admin/header_allows`,
}), }),
providesTags: (res) => providesTags: (res) =>
res res
@ -40,7 +40,7 @@ const extended = gtsApi.injectEndpoints({
getHeaderAllow: build.query<HeaderPermission, string>({ getHeaderAllow: build.query<HeaderPermission, string>({
query: (id) => ({ query: (id) => ({
url: `/api/v1/admin/header_allows/${id}` url: `/api/v1/admin/header_allows/${id}`,
}), }),
providesTags: (_res, _error, id) => [{ type: "HTTPHeaderAllows", id }], providesTags: (_res, _error, id) => [{ type: "HTTPHeaderAllows", id }],
}), }),
@ -51,7 +51,7 @@ const extended = gtsApi.injectEndpoints({
url: `/api/v1/admin/header_allows`, url: `/api/v1/admin/header_allows`,
asForm: true, asForm: true,
body: formData, body: formData,
discardEmpty: true discardEmpty: true,
}), }),
invalidatesTags: [{ type: "HTTPHeaderAllows", id: "LIST" }], invalidatesTags: [{ type: "HTTPHeaderAllows", id: "LIST" }],
}), }),
@ -59,7 +59,7 @@ const extended = gtsApi.injectEndpoints({
deleteHeaderAllow: build.mutation<HeaderPermission, string>({ deleteHeaderAllow: build.mutation<HeaderPermission, string>({
query: (id) => ({ query: (id) => ({
method: "DELETE", method: "DELETE",
url: `/api/v1/admin/header_allows/${id}` url: `/api/v1/admin/header_allows/${id}`,
}), }),
invalidatesTags: (_res, _error, id) => [{ type: "HTTPHeaderAllows", id }], invalidatesTags: (_res, _error, id) => [{ type: "HTTPHeaderAllows", id }],
}), }),
@ -68,7 +68,7 @@ const extended = gtsApi.injectEndpoints({
getHeaderBlocks: build.query<HeaderPermission[], void>({ getHeaderBlocks: build.query<HeaderPermission[], void>({
query: () => ({ query: () => ({
url: `/api/v1/admin/header_blocks` url: `/api/v1/admin/header_blocks`,
}), }),
providesTags: (res) => providesTags: (res) =>
res res
@ -85,14 +85,14 @@ const extended = gtsApi.injectEndpoints({
url: `/api/v1/admin/header_blocks`, url: `/api/v1/admin/header_blocks`,
asForm: true, asForm: true,
body: formData, body: formData,
discardEmpty: true discardEmpty: true,
}), }),
invalidatesTags: [{ type: "HTTPHeaderBlocks", id: "LIST" }], invalidatesTags: [{ type: "HTTPHeaderBlocks", id: "LIST" }],
}), }),
getHeaderBlock: build.query<HeaderPermission, string>({ getHeaderBlock: build.query<HeaderPermission, string>({
query: (id) => ({ query: (id) => ({
url: `/api/v1/admin/header_blocks/${id}` url: `/api/v1/admin/header_blocks/${id}`,
}), }),
providesTags: (_res, _error, id) => [{ type: "HTTPHeaderBlocks", id }], providesTags: (_res, _error, id) => [{ type: "HTTPHeaderBlocks", id }],
}), }),
@ -100,7 +100,7 @@ const extended = gtsApi.injectEndpoints({
deleteHeaderBlock: build.mutation<HeaderPermission, string>({ deleteHeaderBlock: build.mutation<HeaderPermission, string>({
query: (id) => ({ query: (id) => ({
method: "DELETE", method: "DELETE",
url: `/api/v1/admin/header_blocks/${id}` url: `/api/v1/admin/header_blocks/${id}`,
}), }),
invalidatesTags: (_res, _error, id) => [{ type: "HTTPHeaderBlocks", id }], invalidatesTags: (_res, _error, id) => [{ type: "HTTPHeaderBlocks", id }],
}), }),

View File

@ -20,8 +20,8 @@
import { replaceCacheOnMutation, removeFromCacheOnMutation } from "../query-modifiers"; import { replaceCacheOnMutation, removeFromCacheOnMutation } from "../query-modifiers";
import { gtsApi } from "../gts-api"; import { gtsApi } from "../gts-api";
import { listToKeyedObject } from "../transforms"; import { listToKeyedObject } from "../transforms";
import { ActionAccountParams, AdminAccount, HandleSignupParams, SearchAccountParams, SearchAccountResp } from "../../types/account"; import type { ActionAccountParams, AdminAccount, HandleSignupParams, SearchAccountParams, SearchAccountResp } from "../../types/account";
import { InstanceRule, MappedRules } from "../../types/rules"; import type { InstanceRule, MappedRules } from "../../types/rules";
import parse from "parse-link-header"; import parse from "parse-link-header";
const extended = gtsApi.injectEndpoints({ const extended = gtsApi.injectEndpoints({
@ -32,17 +32,17 @@ const extended = gtsApi.injectEndpoints({
url: `/api/v1/instance`, url: `/api/v1/instance`,
asForm: true, asForm: true,
body: formData, body: formData,
discardEmpty: true discardEmpty: true,
}), }),
...replaceCacheOnMutation("instanceV1"), ...replaceCacheOnMutation("instanceV1"),
}), }),
getAccount: build.query<AdminAccount, string>({ getAccount: build.query<AdminAccount, string>({
query: (id) => ({ query: (id) => ({
url: `/api/v1/admin/accounts/${id}` url: `/api/v1/admin/accounts/${id}`,
}), }),
providesTags: (_result, _error, id) => [ providesTags: (_result, _error, id) => [
{ type: 'Account', id } { type: 'Account', id },
], ],
}), }),
@ -61,7 +61,7 @@ const extended = gtsApi.injectEndpoints({
} }
return { return {
url: `/api/v2/admin/accounts${query}` url: `/api/v2/admin/accounts${query}`,
}; };
}, },
// Headers required for paging. // Headers required for paging.
@ -73,7 +73,7 @@ const extended = gtsApi.injectEndpoints({
}, },
// Only provide LIST tag id since this model is not the // Only provide LIST tag id since this model is not the
// same as getAccount model (due to transformResponse). // same as getAccount model (due to transformResponse).
providesTags: [{ type: "Account", id: "TRANSFORMED" }] providesTags: [{ type: "Account", id: "TRANSFORMED" }],
}), }),
actionAccount: build.mutation<string, ActionAccountParams>({ actionAccount: build.mutation<string, ActionAccountParams>({
@ -83,8 +83,8 @@ const extended = gtsApi.injectEndpoints({
asForm: true, asForm: true,
body: { body: {
type: action, type: action,
text: reason text: reason,
} },
}), }),
// Do an optimistic update on this account to mark // Do an optimistic update on this account to mark
// it according to whatever action was submitted. // it according to whatever action was submitted.
@ -95,7 +95,7 @@ const extended = gtsApi.injectEndpoints({
draft.suspended = true; draft.suspended = true;
draft.account.suspended = true; draft.account.suspended = true;
} }
}) }),
); );
// Revert optimistic // Revert optimistic
@ -105,7 +105,7 @@ const extended = gtsApi.injectEndpoints({
} catch { } catch {
patchResult.undo(); patchResult.undo();
} }
} },
}), }),
handleSignup: build.mutation<AdminAccount, HandleSignupParams>({ handleSignup: build.mutation<AdminAccount, HandleSignupParams>({
@ -124,7 +124,7 @@ const extended = gtsApi.injectEndpoints({
// Just invalidate this ID and getAccounts. // Just invalidate this ID and getAccounts.
dispatch(extended.util.invalidateTags([ dispatch(extended.util.invalidateTags([
{ type: "Account", id: id }, { type: "Account", id: id },
{ type: "Account", id: "TRANSFORMED" } { type: "Account", id: "TRANSFORMED" },
])); ]));
return; return;
} }
@ -132,7 +132,7 @@ const extended = gtsApi.injectEndpoints({
const patchResult = dispatch( const patchResult = dispatch(
extended.util.updateQueryData("getAccount", id, (draft) => { extended.util.updateQueryData("getAccount", id, (draft) => {
draft.approved = true; draft.approved = true;
}) }),
); );
// Revert optimistic // Revert optimistic
@ -142,14 +142,14 @@ const extended = gtsApi.injectEndpoints({
} catch { } catch {
patchResult.undo(); patchResult.undo();
} }
} },
}), }),
instanceRules: build.query<MappedRules, void>({ instanceRules: build.query<MappedRules, void>({
query: () => ({ query: () => ({
url: `/api/v1/admin/instance/rules` url: `/api/v1/admin/instance/rules`,
}), }),
transformResponse: listToKeyedObject<InstanceRule>("id") transformResponse: listToKeyedObject<InstanceRule>("id"),
}), }),
addInstanceRule: build.mutation<MappedRules, any>({ addInstanceRule: build.mutation<MappedRules, any>({
@ -158,7 +158,7 @@ const extended = gtsApi.injectEndpoints({
url: `/api/v1/admin/instance/rules`, url: `/api/v1/admin/instance/rules`,
asForm: true, asForm: true,
body: formData, body: formData,
discardEmpty: true discardEmpty: true,
}), }),
transformResponse: listToKeyedObject<InstanceRule>("id"), transformResponse: listToKeyedObject<InstanceRule>("id"),
...replaceCacheOnMutation("instanceRules"), ...replaceCacheOnMutation("instanceRules"),
@ -170,11 +170,11 @@ const extended = gtsApi.injectEndpoints({
url: `/api/v1/admin/instance/rules/${id}`, url: `/api/v1/admin/instance/rules/${id}`,
asForm: true, asForm: true,
body: edit, body: edit,
discardEmpty: true discardEmpty: true,
}), }),
transformResponse: (data) => { transformResponse: (data) => {
return { return {
[data.id]: data [data.id]: data,
}; };
}, },
...replaceCacheOnMutation("instanceRules"), ...replaceCacheOnMutation("instanceRules"),
@ -183,13 +183,13 @@ const extended = gtsApi.injectEndpoints({
deleteInstanceRule: build.mutation({ deleteInstanceRule: build.mutation({
query: (id) => ({ query: (id) => ({
method: "DELETE", method: "DELETE",
url: `/api/v1/admin/instance/rules/${id}` url: `/api/v1/admin/instance/rules/${id}`,
}), }),
...removeFromCacheOnMutation("instanceRules", { ...removeFromCacheOnMutation("instanceRules", {
key: (_draft, rule) => rule.id, key: (_draft, rule) => rule.id,
}) }),
}) }),
}) }),
}); });
export const { export const {

View File

@ -44,7 +44,7 @@ const extended = gtsApi.injectEndpoints({
} }
return { return {
url: `/api/v1/admin/reports${query}` url: `/api/v1/admin/reports${query}`,
}; };
}, },
// Headers required for paging. // Headers required for paging.
@ -56,15 +56,15 @@ const extended = gtsApi.injectEndpoints({
}, },
// Only provide LIST tag id since this model is not the // Only provide LIST tag id since this model is not the
// same as getReport model (due to transformResponse). // same as getReport model (due to transformResponse).
providesTags: [{ type: "Report", id: "TRANSFORMED" }] providesTags: [{ type: "Report", id: "TRANSFORMED" }],
}), }),
getReport: build.query<AdminReport, string>({ getReport: build.query<AdminReport, string>({
query: (id) => ({ query: (id) => ({
url: `/api/v1/admin/reports/${id}` url: `/api/v1/admin/reports/${id}`,
}), }),
providesTags: (_result, _error, id) => [ providesTags: (_result, _error, id) => [
{ type: 'Report', id } { type: 'Report', id },
], ],
}), }),
@ -73,14 +73,14 @@ const extended = gtsApi.injectEndpoints({
url: `/api/v1/admin/reports/${formData.id}/resolve`, url: `/api/v1/admin/reports/${formData.id}/resolve`,
method: "POST", method: "POST",
asForm: true, asForm: true,
body: formData body: formData,
}), }),
invalidatesTags: (res) => invalidatesTags: (res) =>
res res
? [{ type: "Report", id: "TRANSFORMED" }, { type: "Report", id: res.id }] ? [{ type: "Report", id: "TRANSFORMED" }, { type: "Report", id: res.id }]
: [{ type: "Report", id: "TRANSFORMED" }] : [{ type: "Report", id: "TRANSFORMED" }],
}) }),
}) }),
}); });
/** /**

View File

@ -26,7 +26,7 @@ import type {
import { serialize as serializeForm } from "object-to-formdata"; import { serialize as serializeForm } from "object-to-formdata";
import type { FetchBaseQueryMeta } from "@reduxjs/toolkit/dist/query/fetchBaseQuery"; import type { FetchBaseQueryMeta } from "@reduxjs/toolkit/dist/query/fetchBaseQuery";
import type { RootState } from '../../redux/store'; import type { RootState } from '../../redux/store';
import { InstanceV1 } from '../types/instance'; import type { InstanceV1 } from '../types/instance';
/** /**
* GTSFetchArgs extends standard FetchArgs used by * GTSFetchArgs extends standard FetchArgs used by
@ -173,10 +173,10 @@ export const gtsApi = createApi({
endpoints: (build) => ({ endpoints: (build) => ({
instanceV1: build.query<InstanceV1, void>({ instanceV1: build.query<InstanceV1, void>({
query: () => ({ query: () => ({
url: `/api/v1/instance` url: `/api/v1/instance`,
}) }),
}) }),
}) }),
}); });
/** /**

View File

@ -25,8 +25,8 @@ import {
remove as oauthRemove, remove as oauthRemove,
authorize as oauthAuthorize, authorize as oauthAuthorize,
} from "../../../redux/oauth"; } from "../../../redux/oauth";
import { RootState } from '../../../redux/store'; import type { RootState } from '../../../redux/store';
import { Account } from '../../types/account'; import type { Account } from '../../types/account';
export interface OauthTokenRequestBody { export interface OauthTokenRequestBody {
client_id: string; client_id: string;
@ -45,7 +45,7 @@ function getSettingsURL() {
Also drops anything past /settings/, because authorization urls that are too long Also drops anything past /settings/, because authorization urls that are too long
get rejected by GTS. get rejected by GTS.
*/ */
let [pre, _past] = window.location.pathname.split("/settings"); const [pre, _past] = window.location.pathname.split("/settings");
return `${window.location.origin}${pre}/settings`; return `${window.location.origin}${pre}/settings`;
} }
@ -71,14 +71,14 @@ const extended = gtsApi.injectEndpoints({
// return a standard verify_credentials query. // return a standard verify_credentials query.
if (oauthState.loginState != 'callback') { if (oauthState.loginState != 'callback') {
return fetchWithBQ({ return fetchWithBQ({
url: `/api/v1/accounts/verify_credentials` url: `/api/v1/accounts/verify_credentials`,
}); });
} }
// We're in the middle of an auth/callback flow. // We're in the middle of an auth/callback flow.
// Try to retrieve callback code from URL query. // Try to retrieve callback code from URL query.
let urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
let code = urlParams.get("code"); const code = urlParams.get("code");
if (code == undefined) { if (code == undefined) {
return { return {
error: { error: {
@ -91,7 +91,7 @@ const extended = gtsApi.injectEndpoints({
// Retrieve app with which the // Retrieve app with which the
// callback code was generated. // callback code was generated.
let app = oauthState.app; const app = oauthState.app;
if (app == undefined || app.client_id == undefined) { if (app == undefined || app.client_id == undefined) {
return { return {
error: { error: {
@ -109,7 +109,7 @@ const extended = gtsApi.injectEndpoints({
client_secret: app.client_secret, client_secret: app.client_secret,
redirect_uri: SETTINGS_URL, redirect_uri: SETTINGS_URL,
grant_type: "authorization_code", grant_type: "authorization_code",
code: code code: code,
}; };
const tokenResult = await fetchWithBQ({ const tokenResult = await fetchWithBQ({
@ -118,7 +118,7 @@ const extended = gtsApi.injectEndpoints({
body: tokenReqBody, body: tokenReqBody,
}); });
if (tokenResult.error) { if (tokenResult.error) {
return { error: tokenResult.error as FetchBaseQueryError }; return { error: tokenResult.error };
} }
// Remove ?code= query param from // Remove ?code= query param from
@ -131,9 +131,9 @@ const extended = gtsApi.injectEndpoints({
// We're now authed! So return // We're now authed! So return
// standard verify_credentials query. // standard verify_credentials query.
return fetchWithBQ({ return fetchWithBQ({
url: `/api/v1/accounts/verify_credentials` url: `/api/v1/accounts/verify_credentials`,
}); });
} },
}), }),
authorizeFlow: build.mutation({ authorizeFlow: build.mutation({
@ -147,7 +147,7 @@ const extended = gtsApi.injectEndpoints({
} }
instanceUrl = new URL(formData.instance).origin; instanceUrl = new URL(formData.instance).origin;
if (oauthState?.instanceUrl == instanceUrl && oauthState.app) { if (oauthState.instanceUrl == instanceUrl && oauthState.app) {
return { data: oauthState.app }; return { data: oauthState.app };
} }
@ -159,31 +159,31 @@ const extended = gtsApi.injectEndpoints({
client_name: "GoToSocial Settings", client_name: "GoToSocial Settings",
scopes: formData.scopes, scopes: formData.scopes,
redirect_uris: SETTINGS_URL, redirect_uris: SETTINGS_URL,
website: SETTINGS_URL website: SETTINGS_URL,
} },
}); });
if (appResult.error) { if (appResult.error) {
return { error: appResult.error as FetchBaseQueryError }; return { error: appResult.error };
} }
let app = appResult.data as any; const app = appResult.data;
app.scopes = formData.scopes; app.scopes = formData.scopes;
api.dispatch(oauthAuthorize({ api.dispatch(oauthAuthorize({
instanceUrl: instanceUrl, instanceUrl: instanceUrl,
app: app, app: app,
loginState: "callback", loginState: "callback",
expectingRedirect: true expectingRedirect: true,
})); }));
let url = new URL(instanceUrl); const url = new URL(instanceUrl);
url.pathname = "/oauth/authorize"; url.pathname = "/oauth/authorize";
url.searchParams.set("client_id", app.client_id); url.searchParams.set("client_id", app.client_id);
url.searchParams.set("redirect_uri", SETTINGS_URL); url.searchParams.set("redirect_uri", SETTINGS_URL);
url.searchParams.set("response_type", "code"); url.searchParams.set("response_type", "code");
url.searchParams.set("scope", app.scopes); url.searchParams.set("scope", app.scopes);
let redirectURL = url.toString(); const redirectURL = url.toString();
window.location.assign(redirectURL); window.location.assign(redirectURL);
return { data: null }; return { data: null };
}, },
@ -193,9 +193,9 @@ const extended = gtsApi.injectEndpoints({
api.dispatch(oauthRemove()); api.dispatch(oauthRemove());
return { data: null }; return { data: null };
}, },
invalidatesTags: ["Auth"] invalidatesTags: ["Auth"],
}) }),
}) }),
}); });
export const { export const {

View File

@ -86,13 +86,13 @@ function makeCacheMutation(action: Action): CacheMutation {
key = key(draft, newData); key = key(draft, newData);
} }
action(draft, newData, { key }); action(draft, newData, { key });
}) }),
); );
} catch (e) { } catch (e) {
// eslint-disable-next-line no-console
console.error(`rolling back pessimistic update of ${queryName}: ${JSON.stringify(e)}`); console.error(`rolling back pessimistic update of ${queryName}: ${JSON.stringify(e)}`);
} }
} },
}; };
}; };
} }

View File

@ -20,15 +20,15 @@
import fileDownload from "js-file-download"; import fileDownload from "js-file-download";
import { gtsApi } from "../gts-api"; import { gtsApi } from "../gts-api";
import { FetchBaseQueryError } from "@reduxjs/toolkit/query"; import type { FetchBaseQueryError } from "@reduxjs/toolkit/query";
import { AccountExportStats } from "../../types/account"; import type { AccountExportStats } from "../../types/account";
const extended = gtsApi.injectEndpoints({ const extended = gtsApi.injectEndpoints({
endpoints: (build) => ({ endpoints: (build) => ({
exportStats: build.query<AccountExportStats, void>({ exportStats: build.query<AccountExportStats, void>({
query: () => ({ query: () => ({
url: `/api/v1/exports/stats` url: `/api/v1/exports/stats`,
}) }),
}), }),
exportFollowing: build.mutation<string | null, void>({ exportFollowing: build.mutation<string | null, void>({
@ -38,7 +38,7 @@ const extended = gtsApi.injectEndpoints({
acceptContentType: "text/csv", acceptContentType: "text/csv",
}); });
if (csvRes.error) { if (csvRes.error) {
return { error: csvRes.error as FetchBaseQueryError }; return { error: csvRes.error };
} }
if (csvRes.meta?.response?.status !== 200) { if (csvRes.meta?.response?.status !== 200) {
@ -47,7 +47,7 @@ const extended = gtsApi.injectEndpoints({
fileDownload(csvRes.data, "following.csv", "text/csv"); fileDownload(csvRes.data, "following.csv", "text/csv");
return { data: null }; return { data: null };
} },
}), }),
exportFollowers: build.mutation<string | null, void>({ exportFollowers: build.mutation<string | null, void>({
@ -57,7 +57,7 @@ const extended = gtsApi.injectEndpoints({
acceptContentType: "text/csv", acceptContentType: "text/csv",
}); });
if (csvRes.error) { if (csvRes.error) {
return { error: csvRes.error as FetchBaseQueryError }; return { error: csvRes.error };
} }
if (csvRes.meta?.response?.status !== 200) { if (csvRes.meta?.response?.status !== 200) {
@ -66,7 +66,7 @@ const extended = gtsApi.injectEndpoints({
fileDownload(csvRes.data, "followers.csv", "text/csv"); fileDownload(csvRes.data, "followers.csv", "text/csv");
return { data: null }; return { data: null };
} },
}), }),
exportLists: build.mutation<string | null, void>({ exportLists: build.mutation<string | null, void>({
@ -76,7 +76,7 @@ const extended = gtsApi.injectEndpoints({
acceptContentType: "text/csv", acceptContentType: "text/csv",
}); });
if (csvRes.error) { if (csvRes.error) {
return { error: csvRes.error as FetchBaseQueryError }; return { error: csvRes.error };
} }
if (csvRes.meta?.response?.status !== 200) { if (csvRes.meta?.response?.status !== 200) {
@ -85,7 +85,7 @@ const extended = gtsApi.injectEndpoints({
fileDownload(csvRes.data, "lists.csv", "text/csv"); fileDownload(csvRes.data, "lists.csv", "text/csv");
return { data: null }; return { data: null };
} },
}), }),
exportBlocks: build.mutation<string | null, void>({ exportBlocks: build.mutation<string | null, void>({
@ -95,7 +95,7 @@ const extended = gtsApi.injectEndpoints({
acceptContentType: "text/csv", acceptContentType: "text/csv",
}); });
if (csvRes.error) { if (csvRes.error) {
return { error: csvRes.error as FetchBaseQueryError }; return { error: csvRes.error };
} }
if (csvRes.meta?.response?.status !== 200) { if (csvRes.meta?.response?.status !== 200) {
@ -104,7 +104,7 @@ const extended = gtsApi.injectEndpoints({
fileDownload(csvRes.data, "blocks.csv", "text/csv"); fileDownload(csvRes.data, "blocks.csv", "text/csv");
return { data: null }; return { data: null };
} },
}), }),
exportMutes: build.mutation<string | null, void>({ exportMutes: build.mutation<string | null, void>({
@ -114,7 +114,7 @@ const extended = gtsApi.injectEndpoints({
acceptContentType: "text/csv", acceptContentType: "text/csv",
}); });
if (csvRes.error) { if (csvRes.error) {
return { error: csvRes.error as FetchBaseQueryError }; return { error: csvRes.error };
} }
if (csvRes.meta?.response?.status !== 200) { if (csvRes.meta?.response?.status !== 200) {
@ -123,7 +123,7 @@ const extended = gtsApi.injectEndpoints({
fileDownload(csvRes.data, "mutes.csv", "text/csv"); fileDownload(csvRes.data, "mutes.csv", "text/csv");
return { data: null }; return { data: null };
} },
}), }),
importData: build.mutation({ importData: build.mutation({
@ -132,10 +132,10 @@ const extended = gtsApi.injectEndpoints({
url: `/api/v1/import`, url: `/api/v1/import`,
asForm: true, asForm: true,
body: formData, body: formData,
discardEmpty: true discardEmpty: true,
}),
}), }),
}), }),
})
}); });
export const { export const {

View File

@ -21,11 +21,11 @@ import { replaceCacheOnMutation } from "../query-modifiers";
import { gtsApi } from "../gts-api"; import { gtsApi } from "../gts-api";
import type { import type {
MoveAccountFormData, MoveAccountFormData,
UpdateAliasesFormData UpdateAliasesFormData,
} from "../../types/migration"; } from "../../types/migration";
import type { Theme } from "../../types/theme"; import type { Theme } from "../../types/theme";
import { User } from "../../types/user"; import type { User } from "../../types/user";
import { DefaultInteractionPolicies, UpdateDefaultInteractionPolicies } from "../../types/interaction"; import type { DefaultInteractionPolicies, UpdateDefaultInteractionPolicies } from "../../types/interaction";
const extended = gtsApi.injectEndpoints({ const extended = gtsApi.injectEndpoints({
endpoints: (build) => ({ endpoints: (build) => ({
@ -35,36 +35,36 @@ const extended = gtsApi.injectEndpoints({
url: `/api/v1/accounts/update_credentials`, url: `/api/v1/accounts/update_credentials`,
asForm: true, asForm: true,
body: formData, body: formData,
discardEmpty: true discardEmpty: true,
}), }),
...replaceCacheOnMutation("verifyCredentials") ...replaceCacheOnMutation("verifyCredentials"),
}), }),
user: build.query<User, void>({ user: build.query<User, void>({
query: () => ({url: `/api/v1/user`}) query: () => ({url: `/api/v1/user`}),
}), }),
passwordChange: build.mutation({ passwordChange: build.mutation({
query: (data) => ({ query: (data) => ({
method: "POST", method: "POST",
url: `/api/v1/user/password_change`, url: `/api/v1/user/password_change`,
body: data body: data,
}) }),
}), }),
emailChange: build.mutation<User, { password: string, new_email: string }>({ emailChange: build.mutation<User, { password: string, new_email: string }>({
query: (data) => ({ query: (data) => ({
method: "POST", method: "POST",
url: `/api/v1/user/email_change`, url: `/api/v1/user/email_change`,
body: data body: data,
}), }),
...replaceCacheOnMutation("user") ...replaceCacheOnMutation("user"),
}), }),
aliasAccount: build.mutation<any, UpdateAliasesFormData>({ aliasAccount: build.mutation<any, UpdateAliasesFormData>({
async queryFn(formData, _api, _extraOpts, fetchWithBQ) { async queryFn(formData, _api, _extraOpts, fetchWithBQ) {
// Pull entries out from the hooked form. // Pull entries out from the hooked form.
const entries: String[] = []; const entries: string[] = [];
formData.also_known_as_uris.forEach(entry => { formData.also_known_as_uris.forEach(entry => {
if (entry) { if (entry) {
entries.push(entry); entries.push(entry);
@ -76,28 +76,28 @@ const extended = gtsApi.injectEndpoints({
url: `/api/v1/accounts/alias`, url: `/api/v1/accounts/alias`,
body: { also_known_as_uris: entries }, body: { also_known_as_uris: entries },
}); });
} },
}), }),
moveAccount: build.mutation<any, MoveAccountFormData>({ moveAccount: build.mutation<any, MoveAccountFormData>({
query: (data) => ({ query: (data) => ({
method: "POST", method: "POST",
url: `/api/v1/accounts/move`, url: `/api/v1/accounts/move`,
body: data body: data,
}) }),
}), }),
accountThemes: build.query<Theme[], void>({ accountThemes: build.query<Theme[], void>({
query: () => ({ query: () => ({
url: `/api/v1/accounts/themes` url: `/api/v1/accounts/themes`,
}) }),
}), }),
defaultInteractionPolicies: build.query<DefaultInteractionPolicies, void>({ defaultInteractionPolicies: build.query<DefaultInteractionPolicies, void>({
query: () => ({ query: () => ({
url: `/api/v1/interaction_policies/defaults` url: `/api/v1/interaction_policies/defaults`,
}), }),
providesTags: ["DefaultInteractionPolicies"] providesTags: ["DefaultInteractionPolicies"],
}), }),
updateDefaultInteractionPolicies: build.mutation<DefaultInteractionPolicies, UpdateDefaultInteractionPolicies>({ updateDefaultInteractionPolicies: build.mutation<DefaultInteractionPolicies, UpdateDefaultInteractionPolicies>({
@ -106,7 +106,7 @@ const extended = gtsApi.injectEndpoints({
url: `/api/v1/interaction_policies/defaults`, url: `/api/v1/interaction_policies/defaults`,
body: data, body: data,
}), }),
...replaceCacheOnMutation("defaultInteractionPolicies") ...replaceCacheOnMutation("defaultInteractionPolicies"),
}), }),
resetDefaultInteractionPolicies: build.mutation<DefaultInteractionPolicies, void>({ resetDefaultInteractionPolicies: build.mutation<DefaultInteractionPolicies, void>({
@ -115,9 +115,9 @@ const extended = gtsApi.injectEndpoints({
url: `/api/v1/interaction_policies/defaults`, url: `/api/v1/interaction_policies/defaults`,
body: {}, body: {},
}), }),
invalidatesTags: ["DefaultInteractionPolicies"] invalidatesTags: ["DefaultInteractionPolicies"],
}),
}), }),
})
}); });
export const { export const {

View File

@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { import type {
InteractionRequest, InteractionRequest,
SearchInteractionRequestsParams, SearchInteractionRequestsParams,
SearchInteractionRequestsResp, SearchInteractionRequestsResp,
@ -33,7 +33,7 @@ const extended = gtsApi.injectEndpoints({
url: `/api/v1/interaction_requests/${id}`, url: `/api/v1/interaction_requests/${id}`,
}), }),
providesTags: (_result, _error, id) => [ providesTags: (_result, _error, id) => [
{ type: 'InteractionRequest', id } { type: 'InteractionRequest', id },
], ],
}), }),
@ -52,7 +52,7 @@ const extended = gtsApi.injectEndpoints({
} }
return { return {
url: `/api/v1/interaction_requests${query}` url: `/api/v1/interaction_requests${query}`,
}; };
}, },
// Headers required for paging. // Headers required for paging.
@ -62,7 +62,7 @@ const extended = gtsApi.injectEndpoints({
const links = parse(linksStr); const links = parse(linksStr);
return { requests, links }; return { requests, links };
}, },
providesTags: [{ type: "InteractionRequest", id: "TRANSFORMED" }] providesTags: [{ type: "InteractionRequest", id: "TRANSFORMED" }],
}), }),
approveInteractionRequest: build.mutation<InteractionRequest, string>({ approveInteractionRequest: build.mutation<InteractionRequest, string>({
@ -73,7 +73,7 @@ const extended = gtsApi.injectEndpoints({
invalidatesTags: (res) => invalidatesTags: (res) =>
res res
? [{ type: "InteractionRequest", id: "TRANSFORMED" }, { type: "InteractionRequest", id: res.id }] ? [{ type: "InteractionRequest", id: "TRANSFORMED" }, { type: "InteractionRequest", id: res.id }]
: [{ type: "InteractionRequest", id: "TRANSFORMED" }] : [{ type: "InteractionRequest", id: "TRANSFORMED" }],
}), }),
rejectInteractionRequest: build.mutation<any, string>({ rejectInteractionRequest: build.mutation<any, string>({
@ -84,9 +84,9 @@ const extended = gtsApi.injectEndpoints({
invalidatesTags: (res) => invalidatesTags: (res) =>
res res
? [{ type: "InteractionRequest", id: "TRANSFORMED" }, { type: "InteractionRequest", id: res.id }] ? [{ type: "InteractionRequest", id: "TRANSFORMED" }, { type: "InteractionRequest", id: res.id }]
: [{ type: "InteractionRequest", id: "TRANSFORMED" }] : [{ type: "InteractionRequest", id: "TRANSFORMED" }],
}),
}), }),
})
}); });
export const { export const {

View File

@ -17,8 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { Links } from "parse-link-header"; import type { Links } from "parse-link-header";
import { CustomEmoji } from "./custom-emoji"; import type { CustomEmoji } from "./custom-emoji";
export interface AdminAccount { export interface AdminAccount {
id: string, id: string,

View File

@ -18,7 +18,7 @@
*/ */
import typia from "typia"; import typia from "typia";
import { PermType } from "./perm"; import type { PermType } from "./perm";
export const validateDomainPerms = typia.createValidate<DomainPerm[]>(); export const validateDomainPerms = typia.createValidate<DomainPerm[]>();

View File

@ -36,7 +36,7 @@ export interface InstanceV1 {
urls: InstanceUrls; urls: InstanceUrls;
stats: InstanceStats; stats: InstanceStats;
thumbnail: string; thumbnail: string;
contact_account: Object; // TODO: define this. contact_account: object; // TODO: define this.
max_toot_chars: number; max_toot_chars: number;
rules: any[]; // TODO: define this rules: any[]; // TODO: define this
terms?: string; terms?: string;

View File

@ -17,9 +17,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { Links } from "parse-link-header"; import type { Links } from "parse-link-header";
import { Account } from "./account"; import type { Account } from "./account";
import { Status } from "./status"; import type { Status } from "./status";
export interface DefaultInteractionPolicies { export interface DefaultInteractionPolicies {
direct: InteractionPolicy; direct: InteractionPolicy;

View File

@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { Draft } from "@reduxjs/toolkit"; import type { Draft } from "@reduxjs/toolkit";
/** /**
* Pass into a query when you don't * Pass into a query when you don't

View File

@ -17,9 +17,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { Links } from "parse-link-header"; import type { Links } from "parse-link-header";
import { AdminAccount } from "./account"; import type { AdminAccount } from "./account";
import { Status } from "./status"; import type { Status } from "./status";
/** /**
* Admin model of a report. Differs from the client * Admin model of a report. Differs from the client
@ -83,7 +83,7 @@ export interface AdminReport {
* Rules broken according to the reporter, if any. * Rules broken according to the reporter, if any.
* TODO: model this properly. * TODO: model this properly.
*/ */
rules: Object[]; rules: object[];
/** /**
* Comment stored about what action (if any) was taken. * Comment stored about what action (if any) was taken.
*/ */

View File

@ -17,8 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { Account } from "./account"; import type { Account } from "./account";
import { CustomEmoji } from "./custom-emoji"; import type { CustomEmoji } from "./custom-emoji";
export interface Status { export interface Status {
id: string; id: string;

View File

@ -29,7 +29,7 @@ import { get } from "psl";
export function isValidDomainPermission(domain: string): boolean { export function isValidDomainPermission(domain: string): boolean {
return isValidDomain(domain, { return isValidDomain(domain, {
wildcard: false, wildcard: false,
allowUnicode: true allowUnicode: true,
}); });
} }

View File

@ -19,7 +19,7 @@
import { useMemo } from "react"; import { useMemo } from "react";
import { AdminAccount } from "../types/account"; import type { AdminAccount } from "../types/account";
import { store } from "../../redux/store"; import { store } from "../../redux/store";
export function yesOrNo(b: boolean): string { export function yesOrNo(b: boolean): string {

View File

@ -17,7 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { PayloadAction, createSlice } from "@reduxjs/toolkit"; import type { PayloadAction} from "@reduxjs/toolkit";
import { createSlice } from "@reduxjs/toolkit";
import type { Checkable } from "../lib/form/types"; import type { Checkable } from "../lib/form/types";
import { useReducer } from "react"; import { useReducer } from "react";
@ -57,12 +58,12 @@ function initialHookState({
} }
return [ key, { ...entry, key, checked } ]; return [ key, { ...entry, key, checked } ];
}) }),
); );
return { return {
entries: mappedEntries, entries: mappedEntries,
selectedEntries selectedEntries,
}; };
} }
@ -81,7 +82,7 @@ const checklistSlice = createSlice({
} }
return [entry.key, { ...entry, checked } ]; return [entry.key, { ...entry, checked } ];
}) }),
); );
return { entries, selectedEntries }; return { entries, selectedEntries };
@ -97,7 +98,7 @@ const checklistSlice = createSlice({
state.entries[key] = { state.entries[key] = {
...state.entries[key], ...state.entries[key],
...value ...value,
}; };
}, },
updateMultiple: (state, { payload }: PayloadAction<Array<[key: string, value: Partial<Checkable>]>>) => { updateMultiple: (state, { payload }: PayloadAction<Array<[key: string, value: Partial<Checkable>]>>) => {
@ -112,11 +113,11 @@ const checklistSlice = createSlice({
state.entries[key] = { state.entries[key] = {
...state.entries[key], ...state.entries[key],
...value ...value,
}; };
}); });
} },
} },
}); });
export const actions = checklistSlice.actions; export const actions = checklistSlice.actions;
@ -153,6 +154,6 @@ export const useChecklistReducer = (entries: Checkable[], uniqueKey: string, ini
return useReducer( return useReducer(
checklistSlice.reducer, checklistSlice.reducer,
initialState, initialState,
(_) => initialHookState({ entries, uniqueKey, initialValue }) (_) => initialHookState({ entries, uniqueKey, initialValue }),
); );
}; };

View File

@ -17,7 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { PayloadAction, createSlice } from "@reduxjs/toolkit"; import type { PayloadAction} from "@reduxjs/toolkit";
import { createSlice } from "@reduxjs/toolkit";
/** /**
* OAuthToken represents a response * OAuthToken represents a response
@ -78,8 +79,8 @@ export const oauthSlice = createSlice({
delete state.token; delete state.token;
delete state.app; delete state.app;
state.loginState = "logout"; state.loginState = "logout";
} },
} },
}); });
export const { export const {

View File

@ -50,13 +50,13 @@ const persistedReducer = persistReducer({
// This is a cheeky workaround for // This is a cheeky workaround for
// redux-persist being a stickler. // redux-persist being a stickler.
let anyState = state as any; const anyState = state as any;
if (anyState?.oauth != undefined) { if (anyState?.oauth != undefined) {
anyState.oauth.expectingRedirect = false; anyState.oauth.expectingRedirect = false;
} }
return anyState; return anyState;
} },
}, combinedReducers); }, combinedReducers);
export const store = configureStore({ export const store = configureStore({
@ -71,10 +71,10 @@ export const store = configureStore({
PERSIST, PERSIST,
PURGE, PURGE,
REGISTER, REGISTER,
] ],
} },
}).concat(gtsApi.middleware); }).concat(gtsApi.middleware);
} },
}); });
export const persistor = persistStore(store); export const persistor = persistStore(store);

View File

@ -30,7 +30,7 @@ export default function Test({}) {
const form = { const form = {
email: useTextInput("email", { defaultValue: instance?.email }), email: useTextInput("email", { defaultValue: instance?.email }),
message: useTextInput("message") message: useTextInput("message"),
}; };
const [submit, result] = useFormSubmit(form, useSendTestEmailMutation(), { changedOnly: false }); const [submit, result] = useFormSubmit(form, useSendTestEmailMutation(), { changedOnly: false });

View File

@ -47,7 +47,7 @@ export default function ExpireRemote({}) {
} }
return "invalid domain"; return "invalid domain";
} },
}); });
const [expire, expireResult] = useInstanceKeysExpireMutation(); const [expire, expireResult] = useInstanceKeysExpireMutation();

View File

@ -22,7 +22,7 @@ import { useTextInput } from "../../../../lib/form";
import { useLazyApURLQuery } from "../../../../lib/query/admin/debug"; import { useLazyApURLQuery } from "../../../../lib/query/admin/debug";
import { TextInput } from "../../../../components/form/inputs"; import { TextInput } from "../../../../components/form/inputs";
import MutationButton from "../../../../components/form/mutation-button"; import MutationButton from "../../../../components/form/mutation-button";
import { ApURLResponse } from "../../../../lib/types/debug"; import type { ApURLResponse } from "../../../../lib/types/debug";
import Loading from "../../../../components/loading"; import Loading from "../../../../components/loading";
// Used for syntax highlighting of json result. // Used for syntax highlighting of json result.

View File

@ -17,12 +17,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React, { useMemo, useEffect, PropsWithChildren, ReactElement } from "react"; import type { PropsWithChildren, ReactElement } from "react";
import React, { useMemo, useEffect } from "react";
import { matchSorter } from "match-sorter"; import { matchSorter } from "match-sorter";
import ComboBox from "../../../components/combo-box"; import ComboBox from "../../../components/combo-box";
import { useListEmojiQuery } from "../../../lib/query/admin/custom-emoji"; import { useListEmojiQuery } from "../../../lib/query/admin/custom-emoji";
import { CustomEmoji } from "../../../lib/types/custom-emoji"; import type { CustomEmoji } from "../../../lib/types/custom-emoji";
import { ComboboxFormInputHook } from "../../../lib/form/types"; import type { ComboboxFormInputHook } from "../../../lib/form/types";
import Loading from "../../../components/loading"; import Loading from "../../../components/loading";
import { Error } from "../../../components/error"; import { Error } from "../../../components/error";
@ -97,7 +98,7 @@ export function CategorySelect({ field, children }: PropsWithChildren<CategorySe
aria-hidden="true" aria-hidden="true"
/> />
{categoryName} {categoryName}
</> </>,
]); ]);
}); });

View File

@ -47,7 +47,7 @@ export default function EmojiDetail() {
function EmojiDetailForm({ data: emoji }) { function EmojiDetailForm({ data: emoji }) {
const { data: instance } = useInstanceV1Query(); const { data: instance } = useInstanceV1Query();
const emojiMaxSize = useMemo(() => { const emojiMaxSize = useMemo(() => {
return instance?.configuration?.emojis?.emoji_size_limit ?? 50 * 1024; return instance?.configuration.emojis.emoji_size_limit ?? 50 * 1024;
}, [instance]); }, [instance]);
const baseUrl = useBaseUrl(); const baseUrl = useBaseUrl();
@ -56,8 +56,8 @@ function EmojiDetailForm({ data: emoji }) {
category: useComboBoxInput("category", { source: emoji }), category: useComboBoxInput("category", { source: emoji }),
image: useFileInput("image", { image: useFileInput("image", {
withPreview: true, withPreview: true,
maxSize: emojiMaxSize maxSize: emojiMaxSize,
}) }),
}; };
const [modifyEmoji, result] = useFormSubmit(form, useEditEmojiMutation()); const [modifyEmoji, result] = useFormSubmit(form, useEditEmojiMutation());

View File

@ -17,7 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React, { useMemo, useEffect, ReactNode } from "react"; import type { ReactNode } from "react";
import React, { useMemo, useEffect } from "react";
import { useFileInput, useComboBoxInput } from "../../../../lib/form"; import { useFileInput, useComboBoxInput } from "../../../../lib/form";
import useShortcode from "./use-shortcode"; import useShortcode from "./use-shortcode";
import useFormSubmit from "../../../../lib/form/submit"; import useFormSubmit from "../../../../lib/form/submit";
@ -32,7 +33,7 @@ import prettierBytes from "prettier-bytes";
export default function NewEmojiForm() { export default function NewEmojiForm() {
const { data: instance } = useInstanceV1Query(); const { data: instance } = useInstanceV1Query();
const emojiMaxSize = useMemo(() => { const emojiMaxSize = useMemo(() => {
return instance?.configuration?.emojis?.emoji_size_limit ?? 50 * 1024; return instance?.configuration.emojis.emoji_size_limit ?? 50 * 1024;
}, [instance]); }, [instance]);
const prettierMaxSize = useMemo(() => { const prettierMaxSize = useMemo(() => {
@ -43,7 +44,7 @@ export default function NewEmojiForm() {
shortcode: useShortcode(), shortcode: useShortcode(),
image: useFileInput("image", { image: useFileInput("image", {
withPreview: true, withPreview: true,
maxSize: emojiMaxSize maxSize: emojiMaxSize,
}), }),
category: useComboBoxInput("category"), category: useComboBoxInput("category"),
}; };
@ -59,7 +60,7 @@ export default function NewEmojiForm() {
form.shortcode.reset(); form.shortcode.reset();
form.image.reset(); form.image.reset();
form.category.reset(); form.category.reset();
} },
}, },
); );
@ -70,7 +71,7 @@ export default function NewEmojiForm() {
(form.shortcode.value === undefined || form.shortcode.value.length === 0) && (form.shortcode.value === undefined || form.shortcode.value.length === 0) &&
form.image.value !== undefined form.image.value !== undefined
) { ) {
let [name, _ext] = form.image.value.name.split("."); const [name, _ext] = form.image.value.name.split(".");
form.shortcode.setter(name); form.shortcode.setter(name);
} }

View File

@ -27,7 +27,7 @@ import Loading from "../../../../components/loading";
import { Error } from "../../../../components/error"; import { Error } from "../../../../components/error";
import { TextInput } from "../../../../components/form/inputs"; import { TextInput } from "../../../../components/form/inputs";
import { useListEmojiQuery } from "../../../../lib/query/admin/custom-emoji"; import { useListEmojiQuery } from "../../../../lib/query/admin/custom-emoji";
import { CustomEmoji } from "../../../../lib/types/custom-emoji"; import type { CustomEmoji } from "../../../../lib/types/custom-emoji";
export default function EmojiOverview() { export default function EmojiOverview() {
const { data: emoji = [], isLoading, isError, error } = useListEmojiQuery({ filter: "domain:local" }); const { data: emoji = [], isLoading, isError, error } = useListEmojiQuery({ filter: "domain:local" });
@ -87,7 +87,7 @@ function EmojiList({ emoji }: EmojiListParams) {
// Filter from emojis in this category. // Filter from emojis in this category.
emojiByCategory.forEach((entries, category) => { emojiByCategory.forEach((entries, category) => {
const filteredEntries = matchSorter(entries, filter, { const filteredEntries = matchSorter(entries, filter, {
keys: ["shortcode"] keys: ["shortcode"],
}); });
if (filteredEntries.length == 0) { if (filteredEntries.length == 0) {
@ -164,8 +164,12 @@ function EmojiPreview({ emoji }) {
return ( return (
<img <img
onMouseEnter={() => { setAnimate(true); }} onMouseEnter={() => {
onMouseLeave={() => { setAnimate(false); }} setAnimate(true);
}}
onMouseLeave={() => {
setAnimate(false);
}}
src={animate ? emoji.url : emoji.static_url} src={animate ? emoji.url : emoji.static_url}
alt={emoji.shortcode} alt={emoji.shortcode}
title={emoji.shortcode} title={emoji.shortcode}

View File

@ -26,7 +26,7 @@ const shortcodeRegex = /^\w{2,30}$/;
export default function useShortcode() { export default function useShortcode() {
const { data: emoji = [] } = useListEmojiQuery({ const { data: emoji = [] } = useListEmojiQuery({
filter: "domain:local" filter: "domain:local",
}); });
const emojiCodes = useMemo(() => { const emojiCodes = useMemo(() => {
@ -36,7 +36,9 @@ export default function useShortcode() {
return useTextInput("shortcode", { return useTextInput("shortcode", {
validator: function validateShortcode(code) { validator: function validateShortcode(code) {
// technically invalid, but hacky fix to prevent validation error on page load // technically invalid, but hacky fix to prevent validation error on page load
if (code == "") { return ""; } if (code == "") {
return "";
}
if (emojiCodes.has(code)) { if (emojiCodes.has(code)) {
return "Shortcode already in use"; return "Shortcode already in use";
@ -51,6 +53,6 @@ export default function useShortcode() {
} }
return ""; return "";
} },
}); });
} }

View File

@ -31,7 +31,7 @@ export default function RemoteEmoji() {
const { const {
data: emoji = [], data: emoji = [],
isLoading, isLoading,
error error,
} = useListEmojiQuery({ filter: "domain:local" }); } = useListEmojiQuery({ filter: "domain:local" });
const emojiCodes = useMemo(() => new Set(emoji.map((e) => e.shortcode)), [emoji]); const emojiCodes = useMemo(() => new Set(emoji.map((e) => e.shortcode)), [emoji]);

View File

@ -64,7 +64,7 @@ export default function StealThisLook({ emojiCodes }) {
"fa fa-fw", "fa fa-fw",
(result.isLoading (result.isLoading
? "fa-refresh fa-spin" ? "fa-refresh fa-spin"
: "fa-search") : "fa-search"),
].join(" ")} aria-hidden="true" title="Search" /> ].join(" ")} aria-hidden="true" title="Search" />
<span className="sr-only">Search</span> <span className="sr-only">Search</span>
</button> </button>
@ -108,9 +108,9 @@ function CopyEmojiForm({ localEmojiCodes, type, emojiList }) {
const form = { const form = {
selectedEmoji: useCheckListInput("selectedEmoji", { selectedEmoji: useCheckListInput("selectedEmoji", {
entries: emojiList, entries: emojiList,
uniqueKey: "id" uniqueKey: "id",
}), }),
category: useComboBoxInput("category") category: useComboBoxInput("category"),
}; };
const [formSubmit, result] = useFormSubmit( const [formSubmit, result] = useFormSubmit(
@ -126,18 +126,18 @@ function CopyEmojiForm({ localEmojiCodes, type, emojiList }) {
}); });
form.selectedEmoji.updateMultiple(processed); form.selectedEmoji.updateMultiple(processed);
} }
} },
} },
); );
const buttonsInactive = form.selectedEmoji.someSelected const buttonsInactive = form.selectedEmoji.someSelected
? { ? {
disabled: false, disabled: false,
title: "" title: "",
} }
: { : {
disabled: true, disabled: true,
title: "No emoji selected, cannot perform any actions" title: "No emoji selected, cannot perform any actions",
}; };
const checkListExtraProps = useCallback(() => ({ localEmojiCodes }), [localEmojiCodes]); const checkListExtraProps = useCallback(() => ({ localEmojiCodes }), [localEmojiCodes]);
@ -205,7 +205,7 @@ function EmojiEntry({ entry: emoji, onChange, extraProps: { localEmojiCodes } })
return (emoji.checked && localEmojiCodes.has(code)) return (emoji.checked && localEmojiCodes.has(code))
? "Shortcode already in use" ? "Shortcode already in use"
: ""; : "";
} },
}); });
useEffect(() => { useEffect(() => {

View File

@ -23,7 +23,7 @@ import { useTextInput } from "../../../lib/form";
import useFormSubmit from "../../../lib/form/submit"; import useFormSubmit from "../../../lib/form/submit";
import { TextInput } from "../../../components/form/inputs"; import { TextInput } from "../../../components/form/inputs";
import MutationButton from "../../../components/form/mutation-button"; import MutationButton from "../../../components/form/mutation-button";
import { PermType } from "../../../lib/types/perm"; import type { PermType } from "../../../lib/types/perm";
import { RE2JS } from "re2js"; import { RE2JS } from "re2js";
export default function HeaderPermCreateForm({ permType }: { permType: PermType }) { export default function HeaderPermCreateForm({ permType }: { permType: PermType }) {
@ -44,7 +44,7 @@ export default function HeaderPermCreateForm({ permType }: { permType: PermType
} }
return ""; return "";
} },
}), }),
regex: useTextInput("regex", { regex: useTextInput("regex", {
validator: (val: string) => { validator: (val: string) => {
@ -63,7 +63,7 @@ export default function HeaderPermCreateForm({ permType }: { permType: PermType
} }
return ""; return "";
} },
}), }),
}; };

View File

@ -19,11 +19,11 @@
import React, { useEffect, useMemo } from "react"; import React, { useEffect, useMemo } from "react";
import { useLocation, useParams } from "wouter"; import { useLocation, useParams } from "wouter";
import { PermType } from "../../../lib/types/perm"; import type { PermType } from "../../../lib/types/perm";
import { useDeleteHeaderAllowMutation, useDeleteHeaderBlockMutation, useGetHeaderAllowQuery, useGetHeaderBlockQuery } from "../../../lib/query/admin/http-header-permissions"; import { useDeleteHeaderAllowMutation, useDeleteHeaderBlockMutation, useGetHeaderAllowQuery, useGetHeaderBlockQuery } from "../../../lib/query/admin/http-header-permissions";
import { HeaderPermission } from "../../../lib/types/http-header-permissions"; import type { HeaderPermission } from "../../../lib/types/http-header-permissions";
import { FetchBaseQueryError } from "@reduxjs/toolkit/query"; import type { FetchBaseQueryError } from "@reduxjs/toolkit/query";
import { SerializedError } from "@reduxjs/toolkit"; import type { SerializedError } from "@reduxjs/toolkit";
import Loading from "../../../components/loading"; import Loading from "../../../components/loading";
import { Error } from "../../../components/error"; import { Error } from "../../../components/error";
import { useLazyGetAccountQuery } from "../../../lib/query/admin"; import { useLazyGetAccountQuery } from "../../../lib/query/admin";
@ -42,7 +42,7 @@ Some Test Value
Another Test Value`; Another Test Value`;
export default function HeaderPermDetail() { export default function HeaderPermDetail() {
let params = useParams(); const params = useParams();
if (params.permType !== "blocks" && params.permType !== "allows") { if (params.permType !== "blocks" && params.permType !== "allows") {
throw "unrecognized perm type " + params.permType; throw "unrecognized perm type " + params.permType;
} }
@ -50,7 +50,7 @@ export default function HeaderPermDetail() {
return params.permType?.slice(0, -1) as PermType; return params.permType?.slice(0, -1) as PermType;
}, [params]); }, [params]);
let permID = params.permId as string | undefined; const permID = params.permId;
if (!permID) { if (!permID) {
throw "no perm ID"; throw "no perm ID";
} }
@ -127,7 +127,7 @@ function PermDeets({
return <Loading />; return <Loading />;
} else if (isErrorAccount || account === undefined) { } else if (isErrorAccount || account === undefined) {
// Fall back to account ID. // Fall back to account ID.
return perm?.created_by; return perm.created_by;
} }
return ( return (

View File

@ -21,18 +21,18 @@ import React, { useMemo } from "react";
import { useGetHeaderAllowsQuery, useGetHeaderBlocksQuery } from "../../../lib/query/admin/http-header-permissions"; import { useGetHeaderAllowsQuery, useGetHeaderBlocksQuery } from "../../../lib/query/admin/http-header-permissions";
import { NoArg } from "../../../lib/types/query"; import { NoArg } from "../../../lib/types/query";
import { PageableList } from "../../../components/pageable-list"; import { PageableList } from "../../../components/pageable-list";
import { HeaderPermission } from "../../../lib/types/http-header-permissions"; import type { HeaderPermission } from "../../../lib/types/http-header-permissions";
import { useLocation, useParams } from "wouter"; import { useLocation, useParams } from "wouter";
import { PermType } from "../../../lib/types/perm"; import type { PermType } from "../../../lib/types/perm";
import { FetchBaseQueryError } from "@reduxjs/toolkit/query"; import type { FetchBaseQueryError } from "@reduxjs/toolkit/query";
import { SerializedError } from "@reduxjs/toolkit"; import type { SerializedError } from "@reduxjs/toolkit";
import HeaderPermCreateForm from "./create"; import HeaderPermCreateForm from "./create";
export default function HeaderPermsOverview() { export default function HeaderPermsOverview() {
const [ location, setLocation ] = useLocation(); const [ location, setLocation ] = useLocation();
// Parse perm type from routing params. // Parse perm type from routing params.
let params = useParams(); const params = useParams();
if (params.permType !== "blocks" && params.permType !== "allows") { if (params.permType !== "blocks" && params.permType !== "allows") {
throw "unrecognized perm type " + params.permType; throw "unrecognized perm type " + params.permType;
} }
@ -53,7 +53,7 @@ export default function HeaderPermsOverview() {
isFetching: isFetchingBlocks, isFetching: isFetchingBlocks,
isSuccess: isSuccessBlocks, isSuccess: isSuccessBlocks,
isError: isErrorBlocks, isError: isErrorBlocks,
error: errorBlocks error: errorBlocks,
} = useGetHeaderBlocksQuery(NoArg, { skip: permType !== "block" }); } = useGetHeaderBlocksQuery(NoArg, { skip: permType !== "block" });
const { const {
@ -62,7 +62,7 @@ export default function HeaderPermsOverview() {
isFetching: isFetchingAllows, isFetching: isFetchingAllows,
isSuccess: isSuccessAllows, isSuccess: isSuccessAllows,
isError: isErrorAllows, isError: isErrorAllows,
error: errorAllows error: errorAllows,
} = useGetHeaderAllowsQuery(NoArg, { skip: permType !== "allow" }); } = useGetHeaderAllowsQuery(NoArg, { skip: permType !== "allow" });
const itemToEntry = (perm: HeaderPermission) => { const itemToEntry = (perm: HeaderPermission) => {
@ -77,7 +77,7 @@ export default function HeaderPermsOverview() {
// Store the back location in // Store the back location in
// history so the detail view // history so the detail view
// can use it to return here. // can use it to return here.
state: { backLocation: location } state: { backLocation: location },
}); });
}} }}
role="link" role="link"

View File

@ -56,7 +56,7 @@ function EditInstanceRuleForm({ rule }) {
const baseUrl = useBaseUrl(); const baseUrl = useBaseUrl();
const form = { const form = {
id: useValue("id", rule.id), id: useValue("id", rule.id),
rule: useTextInput("text", { defaultValue: rule.text }) rule: useTextInput("text", { defaultValue: rule.text }),
}; };
const [submitForm, result] = useFormSubmit(form, useUpdateInstanceRuleMutation()); const [submitForm, result] = useFormSubmit(form, useUpdateInstanceRuleMutation());

View File

@ -25,7 +25,7 @@ import { useTextInput } from "../../../lib/form";
import useFormSubmit from "../../../lib/form/submit"; import useFormSubmit from "../../../lib/form/submit";
import { TextArea } from "../../../components/form/inputs"; import { TextArea } from "../../../components/form/inputs";
import MutationButton from "../../../components/form/mutation-button"; import MutationButton from "../../../components/form/mutation-button";
import { InstanceRule, MappedRules } from "../../../lib/types/rules"; import type { InstanceRule, MappedRules } from "../../../lib/types/rules";
import FormWithData from "../../../lib/form/form-with-data"; import FormWithData from "../../../lib/form/form-with-data";
export default function InstanceRules() { export default function InstanceRules() {
@ -46,7 +46,9 @@ function InstanceRulesForm({ data: rules }: { data: MappedRules }) {
const [submitForm, result] = useFormSubmit({ newRule }, useAddInstanceRuleMutation(), { const [submitForm, result] = useFormSubmit({ newRule }, useAddInstanceRuleMutation(), {
changedOnly: true, changedOnly: true,
onFinish: () => newRule.reset() onFinish: () => {
newRule.reset();
},
}); });
return ( return (

View File

@ -24,7 +24,7 @@ import { TextInput, TextArea, FileInput } from "../../../components/form/inputs"
import MutationButton from "../../../components/form/mutation-button"; import MutationButton from "../../../components/form/mutation-button";
import { useInstanceV1Query } from "../../../lib/query/gts-api"; import { useInstanceV1Query } from "../../../lib/query/gts-api";
import { useUpdateInstanceMutation } from "../../../lib/query/admin"; import { useUpdateInstanceMutation } from "../../../lib/query/admin";
import { InstanceV1 } from "../../../lib/types/instance"; import type { InstanceV1 } from "../../../lib/types/instance";
import FormWithData from "../../../lib/form/form-with-data"; import FormWithData from "../../../lib/form/form-with-data";
import useFormSubmit from "../../../lib/form/submit"; import useFormSubmit from "../../../lib/form/submit";
@ -50,7 +50,7 @@ function InstanceSettingsForm({ data: instance }: InstanceSettingsFormProps) {
const form = { const form = {
title: useTextInput("title", { title: useTextInput("title", {
source: instance, source: instance,
validator: (val: string) => val.length <= titleLimit ? "" : `Instance title is ${val.length} characters; must be ${titleLimit} characters or less` validator: (val: string) => val.length <= titleLimit ? "" : `Instance title is ${val.length} characters; must be ${titleLimit} characters or less`,
}), }),
thumbnail: useFileInput("thumbnail", { withPreview: true }), thumbnail: useFileInput("thumbnail", { withPreview: true }),
thumbnailDesc: useTextInput("thumbnail_description", { source: instance }), thumbnailDesc: useTextInput("thumbnail_description", { source: instance }),
@ -58,22 +58,22 @@ function InstanceSettingsForm({ data: instance }: InstanceSettingsFormProps) {
source: instance, source: instance,
// Select "raw" text version of parsed field for editing. // Select "raw" text version of parsed field for editing.
valueSelector: (s: InstanceV1) => s.short_description_text, valueSelector: (s: InstanceV1) => s.short_description_text,
validator: (val: string) => val.length <= shortDescLimit ? "" : `Instance short description is ${val.length} characters; must be ${shortDescLimit} characters or less` validator: (val: string) => val.length <= shortDescLimit ? "" : `Instance short description is ${val.length} characters; must be ${shortDescLimit} characters or less`,
}), }),
description: useTextInput("description", { description: useTextInput("description", {
source: instance, source: instance,
// Select "raw" text version of parsed field for editing. // Select "raw" text version of parsed field for editing.
valueSelector: (s: InstanceV1) => s.description_text, valueSelector: (s: InstanceV1) => s.description_text,
validator: (val: string) => val.length <= descLimit ? "" : `Instance description is ${val.length} characters; must be ${descLimit} characters or less` validator: (val: string) => val.length <= descLimit ? "" : `Instance description is ${val.length} characters; must be ${descLimit} characters or less`,
}), }),
terms: useTextInput("terms", { terms: useTextInput("terms", {
source: instance, source: instance,
// Select "raw" text version of parsed field for editing. // Select "raw" text version of parsed field for editing.
valueSelector: (s: InstanceV1) => s.terms_text, valueSelector: (s: InstanceV1) => s.terms_text,
validator: (val: string) => val.length <= termsLimit ? "" : `Instance terms and conditions is ${val.length} characters; must be ${termsLimit} characters or less` validator: (val: string) => val.length <= termsLimit ? "" : `Instance terms and conditions is ${val.length} characters; must be ${termsLimit} characters or less`,
}), }),
contactUser: useTextInput("contact_username", { source: instance, valueSelector: (s) => s.contact_account?.username }), contactUser: useTextInput("contact_username", { source: instance, valueSelector: (s) => s.contact_account?.username }),
contactEmail: useTextInput("contact_email", { source: instance, valueSelector: (s) => s.email }) contactEmail: useTextInput("contact_email", { source: instance, valueSelector: (s) => s.email }),
}; };
const [submitForm, result] = useFormSubmit(form, useUpdateInstanceMutation()); const [submitForm, result] = useFormSubmit(form, useUpdateInstanceMutation());
@ -109,8 +109,8 @@ function InstanceSettingsForm({ data: instance }: InstanceSettingsFormProps) {
<div className="file-upload-with-preview"> <div className="file-upload-with-preview">
<img <img
className="preview avatar" className="preview avatar"
src={form.thumbnail.previewValue ?? instance?.thumbnail} src={form.thumbnail.previewValue ?? instance.thumbnail}
alt={form.thumbnailDesc.value ?? (instance?.thumbnail ? `Thumbnail image for the instance` : "No instance thumbnail image set")} alt={form.thumbnailDesc.value ?? (instance.thumbnail ? `Thumbnail image for the instance` : "No instance thumbnail image set")}
/> />
<div className="file-input-with-image-description"> <div className="file-input-with-image-description">
<FileInput <FileInput

View File

@ -28,7 +28,7 @@ import {
useBoolInput, useBoolInput,
} from "../../../../lib/form"; } from "../../../../lib/form";
import { Checkbox, Select, TextInput } from "../../../../components/form/inputs"; import { Checkbox, Select, TextInput } from "../../../../components/form/inputs";
import { AdminAccount } from "../../../../lib/types/account"; import type { AdminAccount } from "../../../../lib/types/account";
import { useLocation } from "wouter"; import { useLocation } from "wouter";
export interface AccountActionsProps { export interface AccountActionsProps {
@ -65,7 +65,7 @@ export function AccountActions({ account, backLocation }: AccountActionsProps) {
function ModerateAccount({ account }: { account: AdminAccount }) { function ModerateAccount({ account }: { account: AdminAccount }) {
const form = { const form = {
id: useValue("id", account.id), id: useValue("id", account.id),
reason: useTextInput("text") reason: useTextInput("text"),
}; };
const reallySuspend = useBoolInput("reallySuspend"); const reallySuspend = useBoolInput("reallySuspend");
@ -96,7 +96,7 @@ function ModerateAccount({ account }: { account: AdminAccount }) {
/> />
<div className="action-buttons"> <div className="action-buttons">
<MutationButton <MutationButton
disabled={account.suspended || reallySuspend.value === undefined || reallySuspend.value === false} disabled={account.suspended || reallySuspend.value === undefined || !reallySuspend.value}
label="Suspend" label="Suspend"
name="suspend" name="suspend"
result={result} result={result}
@ -140,7 +140,7 @@ function HandleSignup({ account, backLocation }: { account: AdminAccount, backLo
// redirect to accounts page. // redirect to accounts page.
setLocation(backLocation); setLocation(backLocation);
} }
} },
}); });
return ( return (

View File

@ -22,7 +22,7 @@ import React from "react";
import { useGetAccountQuery } from "../../../../lib/query/admin"; import { useGetAccountQuery } from "../../../../lib/query/admin";
import FormWithData from "../../../../lib/form/form-with-data"; import FormWithData from "../../../../lib/form/form-with-data";
import FakeProfile from "../../../../components/profile"; import FakeProfile from "../../../../components/profile";
import { AdminAccount } from "../../../../lib/types/account"; import type { AdminAccount } from "../../../../lib/types/account";
import { AccountActions } from "./actions"; import { AccountActions } from "./actions";
import { useParams } from "wouter"; import { useParams } from "wouter";
import { useBaseUrl } from "../../../../lib/navigation/util"; import { useBaseUrl } from "../../../../lib/navigation/util";
@ -32,7 +32,7 @@ import { UseOurInstanceAccount, yesOrNo } from "../../../../lib/util";
export default function AccountDetail() { export default function AccountDetail() {
const params: { accountID: string } = useParams(); const params: { accountID: string } = useParams();
const baseUrl = useBaseUrl(); const baseUrl = useBaseUrl();
const backLocation: String = history.state?.backLocation ?? `~${baseUrl}`; const backLocation: string = history.state?.backLocation ?? `~${baseUrl}`;
return ( return (
<div className="account-detail"> <div className="account-detail">

View File

@ -17,12 +17,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React, { ReactNode } from "react"; import type { ReactNode } from "react";
import React from "react";
import { useSearchAccountsQuery } from "../../../../lib/query/admin"; import { useSearchAccountsQuery } from "../../../../lib/query/admin";
import { PageableList } from "../../../../components/pageable-list"; import { PageableList } from "../../../../components/pageable-list";
import { useLocation } from "wouter"; import { useLocation } from "wouter";
import Username from "../../../../components/username"; import Username from "../../../../components/username";
import { AdminAccount } from "../../../../lib/types/account"; import type { AdminAccount } from "../../../../lib/types/account";
export default function AccountsPending() { export default function AccountsPending() {
const [ location, _setLocation ] = useLocation(); const [ location, _setLocation ] = useLocation();

View File

@ -17,7 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React, { ReactNode, useEffect, useMemo } from "react"; import type { ReactNode} from "react";
import React, { useEffect, useMemo } from "react";
import { useLazySearchAccountsQuery } from "../../../../lib/query/admin"; import { useLazySearchAccountsQuery } from "../../../../lib/query/admin";
import { useTextInput } from "../../../../lib/form"; import { useTextInput } from "../../../../lib/form";
@ -25,7 +26,7 @@ import { PageableList } from "../../../../components/pageable-list";
import { Select, TextInput } from "../../../../components/form/inputs"; import { Select, TextInput } from "../../../../components/form/inputs";
import MutationButton from "../../../../components/form/mutation-button"; import MutationButton from "../../../../components/form/mutation-button";
import { useLocation, useSearch } from "wouter"; import { useLocation, useSearch } from "wouter";
import { AdminAccount } from "../../../../lib/types/account"; import type { AdminAccount } from "../../../../lib/types/account";
import Username from "../../../../components/username"; import Username from "../../../../components/username";
import isValidDomain from "is-valid-domain"; import isValidDomain from "is-valid-domain";
@ -66,11 +67,11 @@ export function AccountSearchForm() {
} }
return "invalid domain"; return "invalid domain";
} },
}), }),
email: useTextInput("email", { defaultValue: urlQueryParams.get("email") ?? ""}), email: useTextInput("email", { defaultValue: urlQueryParams.get("email") ?? ""}),
ip: useTextInput("ip", { defaultValue: urlQueryParams.get("ip") ?? ""}), ip: useTextInput("ip", { defaultValue: urlQueryParams.get("ip") ?? ""}),
limit: useTextInput("limit", { defaultValue: urlQueryParams.get("limit") ?? "50"}) limit: useTextInput("limit", { defaultValue: urlQueryParams.get("limit") ?? "50"}),
}; };
// On mount, if urlQueryParams were provided, // On mount, if urlQueryParams were provided,

View File

@ -17,9 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React from "react"; import React, { useMemo } from "react";
import { useMemo } from "react";
import { useLocation, useParams, useSearch } from "wouter"; import { useLocation, useParams, useSearch } from "wouter";
import { useTextInput, useBoolInput } from "../../../lib/form"; import { useTextInput, useBoolInput } from "../../../lib/form";
@ -34,18 +33,18 @@ import MutationButton from "../../../components/form/mutation-button";
import { useDomainAllowsQuery, useDomainBlocksQuery } from "../../../lib/query/admin/domain-permissions/get"; import { useDomainAllowsQuery, useDomainBlocksQuery } from "../../../lib/query/admin/domain-permissions/get";
import { useAddDomainAllowMutation, useAddDomainBlockMutation, useRemoveDomainAllowMutation, useRemoveDomainBlockMutation } from "../../../lib/query/admin/domain-permissions/update"; import { useAddDomainAllowMutation, useAddDomainBlockMutation, useRemoveDomainAllowMutation, useRemoveDomainBlockMutation } from "../../../lib/query/admin/domain-permissions/update";
import { DomainPerm } from "../../../lib/types/domain-permission"; import type { DomainPerm } from "../../../lib/types/domain-permission";
import { NoArg } from "../../../lib/types/query"; import { NoArg } from "../../../lib/types/query";
import { Error } from "../../../components/error"; import { Error } from "../../../components/error";
import { useBaseUrl } from "../../../lib/navigation/util"; import { useBaseUrl } from "../../../lib/navigation/util";
import { PermType } from "../../../lib/types/perm"; import type { PermType } from "../../../lib/types/perm";
import isValidDomain from "is-valid-domain"; import isValidDomain from "is-valid-domain";
export default function DomainPermDetail() { export default function DomainPermDetail() {
const baseUrl = useBaseUrl(); const baseUrl = useBaseUrl();
// Parse perm type from routing params. // Parse perm type from routing params.
let params = useParams(); const params = useParams();
if (params.permType !== "blocks" && params.permType !== "allows") { if (params.permType !== "blocks" && params.permType !== "allows") {
throw "unrecognized perm type " + params.permType; throw "unrecognized perm type " + params.permType;
} }
@ -132,7 +131,7 @@ function DomainPermForm({ defaultDomain, perm, permType }: DomainPermFormProps)
const disabledForm = isExistingPerm const disabledForm = isExistingPerm
? { ? {
disabled: true, disabled: true,
title: "Domain permissions currently cannot be edited." title: "Domain permissions currently cannot be edited.",
} }
: { : {
disabled: false, disabled: false,
@ -164,11 +163,11 @@ function DomainPermForm({ defaultDomain, perm, permType }: DomainPermFormProps)
} }
return "invalid domain"; return "invalid domain";
} },
}), }),
obfuscate: useBoolInput("obfuscate", { source: perm }), obfuscate: useBoolInput("obfuscate", { source: perm }),
commentPrivate: useTextInput("private_comment", { source: perm }), commentPrivate: useTextInput("private_comment", { source: perm }),
commentPublic: useTextInput("public_comment", { source: perm }) commentPublic: useTextInput("public_comment", { source: perm }),
}; };
// Check which perm type we're meant to be handling // Check which perm type we're meant to be handling
@ -221,11 +220,11 @@ function DomainPermForm({ defaultDomain, perm, permType }: DomainPermFormProps)
// but if domain input changes, that doesn't match anymore // but if domain input changes, that doesn't match anymore
// and causes issues later on so, before submitting the form, // and causes issues later on so, before submitting the form,
// silently change url, and THEN submit. // silently change url, and THEN submit.
let correctUrl = `/${permType}s/${form.domain.value}`; const correctUrl = `/${permType}s/${form.domain.value}`;
if (location != correctUrl) { if (location != correctUrl) {
setLocation(correctUrl); setLocation(correctUrl);
} }
return submitForm(e); submitForm(e);
} }
return ( return (

View File

@ -17,9 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React from "react"; import React, { useEffect } from "react";
import { useEffect } from "react";
import { useExportDomainListMutation } from "../../../lib/query/admin/domain-permissions/export"; import { useExportDomainListMutation } from "../../../lib/query/admin/domain-permissions/export";
import useFormSubmit from "../../../lib/form/submit"; import useFormSubmit from "../../../lib/form/submit";
import { import {
@ -96,7 +95,9 @@ export default function ImportExportForm({ form, submitParse, parseResult }: Imp
<MutationButton <MutationButton
label="Import" label="Import"
type="button" type="button"
onClick={() => submitParse()} onClick={() => {
submitParse();
}}
result={parseResult} result={parseResult}
showError={false} showError={false}
disabled={form.permType.value === undefined || form.permType.value.length === 0} disabled={form.permType.value === undefined || form.permType.value.length === 0}
@ -116,7 +117,9 @@ export default function ImportExportForm({ form, submitParse, parseResult }: Imp
<MutationButton <MutationButton
label="Export" label="Export"
type="button" type="button"
onClick={() => submitExport("export")} onClick={() => {
submitExport("export");
}}
result={exportResult} showError={false} result={exportResult} showError={false}
disabled={form.permType.value === undefined || form.permType.value.length === 0} disabled={form.permType.value === undefined || form.permType.value.length === 0}
/> />
@ -124,7 +127,9 @@ export default function ImportExportForm({ form, submitParse, parseResult }: Imp
label="Export to file" label="Export to file"
wrapperClassName="export-file-button" wrapperClassName="export-file-button"
type="button" type="button"
onClick={() => submitExport("export-file")} onClick={() => {
submitExport("export-file");
}}
result={exportResult} result={exportResult}
showError={false} showError={false}
disabled={form.permType.value === undefined || form.permType.value.length === 0} disabled={form.permType.value === undefined || form.permType.value.length === 0}

View File

@ -37,8 +37,8 @@ export default function ImportExport() {
options: { options: {
block: "Domain blocks", block: "Domain blocks",
allow: "Domain allows", allow: "Domain allows",
} },
}) }),
}; };
const [submitParse, parseResult] = useFormSubmit(form, useProcessDomainPermissionsMutation(), { changedOnly: false }); const [submitParse, parseResult] = useFormSubmit(form, useProcessDomainPermissionsMutation(), { changedOnly: false });

View File

@ -17,9 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React from "react"; import React, { useMemo } from "react";
import { useMemo } from "react";
import { Link, useLocation, useParams } from "wouter"; import { Link, useLocation, useParams } from "wouter";
import { matchSorter } from "match-sorter"; import { matchSorter } from "match-sorter";
import { useTextInput } from "../../../lib/form"; import { useTextInput } from "../../../lib/form";
@ -28,14 +27,14 @@ import Loading from "../../../components/loading";
import { useDomainAllowsQuery, useDomainBlocksQuery } from "../../../lib/query/admin/domain-permissions/get"; import { useDomainAllowsQuery, useDomainBlocksQuery } from "../../../lib/query/admin/domain-permissions/get";
import type { MappedDomainPerms } from "../../../lib/types/domain-permission"; import type { MappedDomainPerms } from "../../../lib/types/domain-permission";
import { NoArg } from "../../../lib/types/query"; import { NoArg } from "../../../lib/types/query";
import { PermType } from "../../../lib/types/perm"; import type { PermType } from "../../../lib/types/perm";
import { useBaseUrl } from "../../../lib/navigation/util"; import { useBaseUrl } from "../../../lib/navigation/util";
export default function DomainPermissionsOverview() { export default function DomainPermissionsOverview() {
const baseUrl = useBaseUrl(); const baseUrl = useBaseUrl();
// Parse perm type from routing params. // Parse perm type from routing params.
let params = useParams(); const params = useParams();
if (params.permType !== "blocks" && params.permType !== "allows") { if (params.permType !== "blocks" && params.permType !== "allows") {
throw "unrecognized perm type " + params.permType; throw "unrecognized perm type " + params.permType;
} }

View File

@ -17,8 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React from "react"; import React, { memo, useMemo, useCallback, useEffect } from "react";
import { memo, useMemo, useCallback, useEffect } from "react";
import { isValidDomainPermission, hasBetterScope } from "../../../lib/util/domain-permission"; import { isValidDomainPermission, hasBetterScope } from "../../../lib/util/domain-permission";
import { import {
@ -45,7 +44,7 @@ import FormWithData from "../../../lib/form/form-with-data";
import { useImportDomainPermsMutation } from "../../../lib/query/admin/domain-permissions/import"; import { useImportDomainPermsMutation } from "../../../lib/query/admin/domain-permissions/import";
import { import {
useDomainAllowsQuery, useDomainAllowsQuery,
useDomainBlocksQuery useDomainBlocksQuery,
} from "../../../lib/query/admin/domain-permissions/get"; } from "../../../lib/query/admin/domain-permissions/get";
import type { DomainPerm, MappedDomainPerms } from "../../../lib/types/domain-permission"; import type { DomainPerm, MappedDomainPerms } from "../../../lib/types/domain-permission";
@ -68,7 +67,7 @@ export const ProcessImport = memo(
{...{ list, permType }} {...{ list, permType }}
/> />
); );
} },
); );
export interface ImportListProps { export interface ImportListProps {
@ -111,22 +110,22 @@ function ImportList({ list, data: domainPerms, permType }: ImportListProps) {
domains: useCheckListInput("domains", { entries: list }), // DomainPerm is actually also a Checkable. domains: useCheckListInput("domains", { entries: list }), // DomainPerm is actually also a Checkable.
obfuscate: useBoolInput("obfuscate"), obfuscate: useBoolInput("obfuscate"),
privateComment: useTextInput("private_comment", { privateComment: useTextInput("private_comment", {
defaultValue: `Imported on ${new Date().toLocaleString()}` defaultValue: `Imported on ${new Date().toLocaleString()}`,
}), }),
privateCommentBehavior: useRadioInput("private_comment_behavior", { privateCommentBehavior: useRadioInput("private_comment_behavior", {
defaultValue: "append", defaultValue: "append",
options: { options: {
append: "Append to", append: "Append to",
replace: "Replace" replace: "Replace",
} },
}), }),
publicComment: useTextInput("public_comment"), publicComment: useTextInput("public_comment"),
publicCommentBehavior: useRadioInput("public_comment_behavior", { publicCommentBehavior: useRadioInput("public_comment_behavior", {
defaultValue: "append", defaultValue: "append",
options: { options: {
append: "Append to", append: "Append to",
replace: "Replace" replace: "Replace",
} },
}), }),
permType: permType, permType: permType,
}; };
@ -218,7 +217,7 @@ function DomainCheckList({ field, domainPerms, commentType, permType }: DomainCh
return ( return (
<> <>
<CheckList <CheckList
field={field as ChecklistInputHook} field={field}
header={<> header={<>
<b>Domain</b> <b>Domain</b>
<b> <b>
@ -252,7 +251,7 @@ const UpdateHint = memo(
function changeAll() { function changeAll() {
updateMultiple( updateMultiple(
entries.map((entry) => [entry.key, { domain: entry.suggest, suggest: null }]) entries.map((entry) => [entry.key, { domain: entry.suggest, suggest: null }]),
); );
} }
@ -270,7 +269,7 @@ const UpdateHint = memo(
{entries.length > 0 && <a onClick={changeAll}>change all</a>} {entries.length > 0 && <a onClick={changeAll}>change all</a>}
</div> </div>
); );
} },
); );
interface UpdateableEntryProps { interface UpdateableEntryProps {
@ -290,7 +289,7 @@ const UpdateableEntry = memo(
}>change</a> }>change</a>
</> </>
); );
} },
); );
function domainValidationError(isValid) { function domainValidationError(isValid) {
@ -312,7 +311,7 @@ function DomainEntry({ entry, onChange, extraProps: { alreadyExists, comment, pe
defaultValue: entry.domain, defaultValue: entry.domain,
showValidation: entry.checked, showValidation: entry.checked,
initValidation: domainValidationError(entry.valid), initValidation: domainValidationError(entry.valid),
validator: (value) => domainValidationError(isValidDomainPermission(value)) validator: (value) => domainValidationError(isValidDomainPermission(value)),
}); });
useEffect(() => { useEffect(() => {

View File

@ -28,14 +28,14 @@ import MutationButton from "../../../components/form/mutation-button";
import Username from "../../../components/username"; import Username from "../../../components/username";
import { useGetReportQuery, useResolveReportMutation } from "../../../lib/query/admin/reports"; import { useGetReportQuery, useResolveReportMutation } from "../../../lib/query/admin/reports";
import { useBaseUrl } from "../../../lib/navigation/util"; import { useBaseUrl } from "../../../lib/navigation/util";
import { AdminReport } from "../../../lib/types/report"; import type { AdminReport } from "../../../lib/types/report";
import { yesOrNo } from "../../../lib/util"; import { yesOrNo } from "../../../lib/util";
import { Status } from "../../../components/status"; import { Status } from "../../../components/status";
export default function ReportDetail({ }) { export default function ReportDetail({ }) {
const params: { reportId: string } = useParams(); const params: { reportId: string } = useParams();
const baseUrl = useBaseUrl(); const baseUrl = useBaseUrl();
const backLocation: String = history.state?.backLocation ?? `~${baseUrl}`; const backLocation: string = history.state?.backLocation ?? `~${baseUrl}`;
return ( return (
<div className="report-detail"> <div className="report-detail">
@ -200,7 +200,7 @@ function ReportHistory({ report, baseUrl, location }: ReportSectionProps) {
function ReportActionForm({ report }) { function ReportActionForm({ report }) {
const form = { const form = {
id: useValue("id", report.id), id: useValue("id", report.id),
comment: useTextInput("action_taken_comment") comment: useTextInput("action_taken_comment"),
}; };
const [submit, result] = useFormSubmit(form, useResolveReportMutation(), { changedOnly: false }); const [submit, result] = useFormSubmit(form, useResolveReportMutation(), { changedOnly: false });

View File

@ -17,7 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React, { ReactNode, useEffect, useMemo } from "react"; import type { ReactNode} from "react";
import React, { useEffect, useMemo } from "react";
import { useLazySearchReportsQuery } from "../../../lib/query/admin/reports"; import { useLazySearchReportsQuery } from "../../../lib/query/admin/reports";
import { useTextInput } from "../../../lib/form"; import { useTextInput } from "../../../lib/form";
@ -26,7 +27,7 @@ import { Select } from "../../../components/form/inputs";
import MutationButton from "../../../components/form/mutation-button"; import MutationButton from "../../../components/form/mutation-button";
import { useLocation, useSearch } from "wouter"; import { useLocation, useSearch } from "wouter";
import Username from "../../../components/username"; import Username from "../../../components/username";
import { AdminReport } from "../../../lib/types/report"; import type { AdminReport } from "../../../lib/types/report";
export default function ReportsSearch() { export default function ReportsSearch() {
return ( return (
@ -61,7 +62,7 @@ function ReportSearchForm() {
resolved: useTextInput("resolved", { defaultValue: resolved }), resolved: useTextInput("resolved", { defaultValue: resolved }),
account_id: useTextInput("account_id", { defaultValue: urlQueryParams.get("account_id") ?? "" }), account_id: useTextInput("account_id", { defaultValue: urlQueryParams.get("account_id") ?? "" }),
target_account_id: useTextInput("target_account_id", { defaultValue: urlQueryParams.get("target_account_id") ?? "" }), target_account_id: useTextInput("target_account_id", { defaultValue: urlQueryParams.get("target_account_id") ?? "" }),
limit: useTextInput("limit", { defaultValue: urlQueryParams.get("limit") ?? "20" }) limit: useTextInput("limit", { defaultValue: urlQueryParams.get("limit") ?? "20" }),
}; };
const setResolved = form.resolved.setter; const setResolved = form.resolved.setter;
@ -196,7 +197,7 @@ function ReportListEntry({ report, linkTo, backLocation }: ReportEntryProps) {
// Store the back location in history so // Store the back location in history so
// the detail view can use it to return to // the detail view can use it to return to
// this page (including query parameters). // this page (including query parameters).
state: { backLocation: backLocation } state: { backLocation: backLocation },
}); });
}} }}
role="link" role="link"

View File

@ -24,7 +24,7 @@ import { TextInput } from "../../components/form/inputs";
import MutationButton from "../../components/form/mutation-button"; import MutationButton from "../../components/form/mutation-button";
import { useEmailChangeMutation, usePasswordChangeMutation, useUserQuery } from "../../lib/query/user"; import { useEmailChangeMutation, usePasswordChangeMutation, useUserQuery } from "../../lib/query/user";
import Loading from "../../components/loading"; import Loading from "../../components/loading";
import { User } from "../../lib/types/user"; import type { User } from "../../lib/types/user";
import { useInstanceV1Query } from "../../lib/query/gts-api"; import { useInstanceV1Query } from "../../lib/query/gts-api";
export default function EmailPassword() { export default function EmailPassword() {
@ -42,7 +42,7 @@ function PasswordChange() {
const { const {
data: instance, data: instance,
isFetching: isFetchingInstance, isFetching: isFetchingInstance,
isLoading: isLoadingInstance isLoading: isLoadingInstance,
} = useInstanceV1Query(); } = useInstanceV1Query();
if (isFetchingInstance || isLoadingInstance) { if (isFetchingInstance || isLoadingInstance) {
return <Loading />; return <Loading />;
@ -64,8 +64,8 @@ function PasswordChangeForm({ oidcEnabled }: { oidcEnabled?: boolean }) {
return "New password same as old password"; return "New password same as old password";
} }
return ""; return "";
} },
}) }),
}; };
const verifyNewPassword = useTextInput("verifyNewPassword", { const verifyNewPassword = useTextInput("verifyNewPassword", {
@ -74,7 +74,7 @@ function PasswordChangeForm({ oidcEnabled }: { oidcEnabled?: boolean }) {
return "Passwords do not match"; return "Passwords do not match";
} }
return ""; return "";
} },
}); });
const [submitForm, result] = useFormSubmit(form, usePasswordChangeMutation()); const [submitForm, result] = useFormSubmit(form, usePasswordChangeMutation());
@ -138,14 +138,14 @@ function EmailChange() {
const { const {
data: instance, data: instance,
isFetching: isFetchingInstance, isFetching: isFetchingInstance,
isLoading: isLoadingInstance isLoading: isLoadingInstance,
} = useInstanceV1Query(); } = useInstanceV1Query();
// Load user data. // Load user data.
const { const {
data: user, data: user,
isFetching: isFetchingUser, isFetching: isFetchingUser,
isLoading: isLoadingUser isLoading: isLoadingUser,
} = useUserQuery(); } = useUserQuery();
if ( if (
@ -170,7 +170,7 @@ function EmailChangeForm({user, oidcEnabled}: { user: User, oidcEnabled?: boolea
const form = { const form = {
currentEmail: useTextInput("current_email", { currentEmail: useTextInput("current_email", {
defaultValue: user.email, defaultValue: user.email,
nosubmit: true nosubmit: true,
}), }),
newEmail: useTextInput("new_email", { newEmail: useTextInput("new_email", {
validator: (value: string | undefined) => { validator: (value: string | undefined) => {

View File

@ -28,7 +28,7 @@ import {
import MutationButton from "../../../components/form/mutation-button"; import MutationButton from "../../../components/form/mutation-button";
import useFormSubmit from "../../../lib/form/submit"; import useFormSubmit from "../../../lib/form/submit";
import { useValue } from "../../../lib/form"; import { useValue } from "../../../lib/form";
import { AccountExportStats } from "../../../lib/types/account"; import type { AccountExportStats } from "../../../lib/types/account";
export default function Export({ exportStats }: { exportStats: AccountExportStats }) { export default function Export({ exportStats }: { exportStats: AccountExportStats }) {
const [exportFollowing, exportFollowingResult] = useFormSubmit( const [exportFollowing, exportFollowingResult] = useFormSubmit(
@ -105,7 +105,9 @@ export default function Export({ exportStats }: { exportStats: AccountExportStat
className="text-cutoff" className="text-cutoff"
label="Download following.csv" label="Download following.csv"
type="button" type="button"
onClick={() => exportFollowing()} onClick={() => {
exportFollowing();
}}
result={exportFollowingResult} result={exportFollowingResult}
showError={true} showError={true}
disabled={exportStats.following_count === 0} disabled={exportStats.following_count === 0}
@ -119,7 +121,9 @@ export default function Export({ exportStats }: { exportStats: AccountExportStat
className="text-cutoff" className="text-cutoff"
label="Download followers.csv" label="Download followers.csv"
type="button" type="button"
onClick={() => exportFollowers()} onClick={() => {
exportFollowers();
}}
result={exportFollowersResult} result={exportFollowersResult}
showError={true} showError={true}
disabled={exportStats.followers_count === 0} disabled={exportStats.followers_count === 0}
@ -133,7 +137,9 @@ export default function Export({ exportStats }: { exportStats: AccountExportStat
className="text-cutoff" className="text-cutoff"
label="Download lists.csv" label="Download lists.csv"
type="button" type="button"
onClick={() => exportLists()} onClick={() => {
exportLists();
}}
result={exportListsResult} result={exportListsResult}
showError={true} showError={true}
disabled={exportStats.lists_count === 0} disabled={exportStats.lists_count === 0}
@ -147,7 +153,9 @@ export default function Export({ exportStats }: { exportStats: AccountExportStat
className="text-cutoff" className="text-cutoff"
label="Download blocks.csv" label="Download blocks.csv"
type="button" type="button"
onClick={() => exportBlocks()} onClick={() => {
exportBlocks();
}}
result={exportBlocksResult} result={exportBlocksResult}
showError={true} showError={true}
disabled={exportStats.blocks_count === 0} disabled={exportStats.blocks_count === 0}
@ -161,7 +169,9 @@ export default function Export({ exportStats }: { exportStats: AccountExportStat
className="text-cutoff" className="text-cutoff"
label="Download mutes.csv" label="Download mutes.csv"
type="button" type="button"
onClick={() => exportMutes()} onClick={() => {
exportMutes();
}}
result={exportMutesResult} result={exportMutesResult}
showError={true} showError={true}
disabled={exportStats.mutes_count === 0} disabled={exportStats.mutes_count === 0}

View File

@ -28,7 +28,7 @@ export default function Import() {
const form = { const form = {
data: useFileInput("data"), data: useFileInput("data"),
type: useTextInput("type", { defaultValue: "" }), type: useTextInput("type", { defaultValue: "" }),
mode: useTextInput("mode", { defaultValue: "" }) mode: useTextInput("mode", { defaultValue: "" }),
}; };
const [submitForm, result] = useFormSubmit(form, useImportDataMutation(), { const [submitForm, result] = useFormSubmit(form, useImportDataMutation(), {
@ -37,7 +37,7 @@ export default function Import() {
form.data.reset(); form.data.reset();
form.type.reset(); form.type.reset();
form.mode.reset(); form.mode.reset();
} },
}); });
return ( return (

View File

@ -23,7 +23,7 @@ import FormWithData from "../../../lib/form/form-with-data";
import BackButton from "../../../components/back-button"; import BackButton from "../../../components/back-button";
import { useBaseUrl } from "../../../lib/navigation/util"; import { useBaseUrl } from "../../../lib/navigation/util";
import { useApproveInteractionRequestMutation, useGetInteractionRequestQuery, useRejectInteractionRequestMutation } from "../../../lib/query/user/interactions"; import { useApproveInteractionRequestMutation, useGetInteractionRequestQuery, useRejectInteractionRequestMutation } from "../../../lib/query/user/interactions";
import { InteractionRequest } from "../../../lib/types/interaction"; import type { InteractionRequest } from "../../../lib/types/interaction";
import { useIcon, useNoun, useVerbed } from "./util"; import { useIcon, useNoun, useVerbed } from "./util";
import MutationButton from "../../../components/form/mutation-button"; import MutationButton from "../../../components/form/mutation-button";
import { Status } from "../../../components/status"; import { Status } from "../../../components/status";
@ -31,7 +31,7 @@ import { Status } from "../../../components/status";
export default function InteractionRequestDetail({ }) { export default function InteractionRequestDetail({ }) {
const params: { reqId: string } = useParams(); const params: { reqId: string } = useParams();
const baseUrl = useBaseUrl(); const baseUrl = useBaseUrl();
const backLocation: String = history.state?.backLocation ?? `~${baseUrl}`; const backLocation: string = history.state?.backLocation ?? `~${baseUrl}`;
return ( return (
<div className="interaction-request-detail"> <div className="interaction-request-detail">

View File

@ -17,14 +17,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React, { ReactNode, useEffect, useMemo } from "react"; import type { ReactNode} from "react";
import React, { useEffect, useMemo } from "react";
import { useBoolInput, useTextInput } from "../../../lib/form"; import { useBoolInput, useTextInput } from "../../../lib/form";
import { PageableList } from "../../../components/pageable-list"; import { PageableList } from "../../../components/pageable-list";
import MutationButton from "../../../components/form/mutation-button"; import MutationButton from "../../../components/form/mutation-button";
import { useLocation, useSearch } from "wouter"; import { useLocation, useSearch } from "wouter";
import { useApproveInteractionRequestMutation, useLazySearchInteractionRequestsQuery, useRejectInteractionRequestMutation } from "../../../lib/query/user/interactions"; import { useApproveInteractionRequestMutation, useLazySearchInteractionRequestsQuery, useRejectInteractionRequestMutation } from "../../../lib/query/user/interactions";
import { InteractionRequest } from "../../../lib/types/interaction"; import type { InteractionRequest } from "../../../lib/types/interaction";
import { Checkbox } from "../../../components/form/inputs"; import { Checkbox } from "../../../components/form/inputs";
import { useContent, useIcon, useNoun, useVerbed } from "./util"; import { useContent, useIcon, useNoun, useVerbed } from "./util";
@ -46,16 +47,16 @@ export default function InteractionRequestsSearchForm() {
// urlQueryParams, to allow paging. // urlQueryParams, to allow paging.
const form = { const form = {
statusID: useTextInput("status_id", { statusID: useTextInput("status_id", {
defaultValue: urlQueryParams.get("status_id") ?? "" defaultValue: urlQueryParams.get("status_id") ?? "",
}), }),
likes: useBoolInput("favourites", { likes: useBoolInput("favourites", {
defaultValue: defaultTrue(urlQueryParams.get("favourites")) defaultValue: defaultTrue(urlQueryParams.get("favourites")),
}), }),
replies: useBoolInput("replies", { replies: useBoolInput("replies", {
defaultValue: defaultTrue(urlQueryParams.get("replies")) defaultValue: defaultTrue(urlQueryParams.get("replies")),
}), }),
boosts: useBoolInput("reblogs", { boosts: useBoolInput("reblogs", {
defaultValue: defaultTrue(urlQueryParams.get("reblogs")) defaultValue: defaultTrue(urlQueryParams.get("reblogs")),
}), }),
}; };
@ -186,7 +187,7 @@ function ReqsListEntry({ req, linkTo, backLocation }: ReqsListEntryProps) {
// Store the back location in history so // Store the back location in history so
// the detail view can use it to return to // the detail view can use it to return to
// this page (including query parameters). // this page (including query parameters).
state: { backLocation: backLocation } state: { backLocation: backLocation },
}); });
}} }}
role="link" role="link"

View File

@ -20,8 +20,9 @@
import { useMemo } from "react"; import { useMemo } from "react";
import sanitize from "sanitize-html"; import sanitize from "sanitize-html";
import { compile, HtmlToTextOptions } from "html-to-text"; import type { HtmlToTextOptions } from "html-to-text";
import { Status } from "../../../lib/types/status"; import { compile } from "html-to-text";
import type { Status } from "../../../lib/types/status";
// Options for converting HTML statuses // Options for converting HTML statuses
// to plaintext representations. // to plaintext representations.
@ -29,7 +30,7 @@ const convertOptions: HtmlToTextOptions = {
selectors: [ selectors: [
// Don't fancy format links, just use their text value. // Don't fancy format links, just use their text value.
{ selector: 'a', options: { ignoreHref: true } }, { selector: 'a', options: { ignoreHref: true } },
] ],
}; };
const convertHTML = compile(convertOptions); const convertHTML = compile(convertOptions);

View File

@ -142,8 +142,8 @@ function AlsoKnownAsURI({ index, data }) {
} }
function MoveForm({ data: profile }) { function MoveForm({ data: profile }) {
let urlStr = store.getState().oauth.instanceUrl ?? ""; const urlStr = store.getState().oauth.instanceUrl ?? "";
let url = new URL(urlStr); const url = new URL(urlStr);
const form = { const form = {
movedToURI: useTextInput("moved_to_uri", { movedToURI: useTextInput("moved_to_uri", {

View File

@ -24,7 +24,7 @@ import { Select, Checkbox } from "../../../../components/form/inputs";
import Languages from "../../../../components/languages"; import Languages from "../../../../components/languages";
import MutationButton from "../../../../components/form/mutation-button"; import MutationButton from "../../../../components/form/mutation-button";
import { useUpdateCredentialsMutation } from "../../../../lib/query/user"; import { useUpdateCredentialsMutation } from "../../../../lib/query/user";
import { Account } from "../../../../lib/types/account"; import type { Account } from "../../../../lib/types/account";
export default function BasicSettings({ account }: { account: Account }) { export default function BasicSettings({ account }: { account: Account }) {
/* form keys /* form keys
@ -36,7 +36,7 @@ export default function BasicSettings({ account }: { account: Account }) {
const form = { const form = {
defaultPrivacy: useTextInput("source[privacy]", { source: account, defaultValue: "unlisted" }), defaultPrivacy: useTextInput("source[privacy]", { source: account, defaultValue: "unlisted" }),
isSensitive: useBoolInput("source[sensitive]", { source: account }), isSensitive: useBoolInput("source[sensitive]", { source: account }),
language: useTextInput("source[language]", { source: account, valueSelector: (s: Account) => s.source?.language?.toUpperCase() ?? "EN" }), language: useTextInput("source[language]", { source: account, valueSelector: (s: Account) => s.source?.language.toUpperCase() ?? "EN" }),
statusContentType: useTextInput("source[status_content_type]", { source: account, defaultValue: "text/plain" }), statusContentType: useTextInput("source[status_content_type]", { source: account, defaultValue: "text/plain" }),
}; };

View File

@ -18,15 +18,16 @@
*/ */
import React, { useMemo } from "react"; import React, { useMemo } from "react";
import type {
InteractionPolicyValue} from "../../../../lib/types/interaction";
import { import {
InteractionPolicyValue,
PolicyValueAuthor, PolicyValueAuthor,
PolicyValueFollowers, PolicyValueFollowers,
PolicyValueMentioned, PolicyValueMentioned,
PolicyValuePublic, PolicyValuePublic,
} from "../../../../lib/types/interaction"; } from "../../../../lib/types/interaction";
import { useTextInput } from "../../../../lib/form"; import { useTextInput } from "../../../../lib/form";
import { Action, BasicValue, PolicyFormSub, Visibility } from "./types"; import type { Action, BasicValue, PolicyFormSub, Visibility } from "./types";
// Based on the given visibility, action, and states, // Based on the given visibility, action, and states,
// derives what the initial basic Select value should be. // derives what the initial basic Select value should be.

View File

@ -26,11 +26,12 @@ import {
import Loading from "../../../../components/loading"; import Loading from "../../../../components/loading";
import { Error } from "../../../../components/error"; import { Error } from "../../../../components/error";
import MutationButton from "../../../../components/form/mutation-button"; import MutationButton from "../../../../components/form/mutation-button";
import { import type {
DefaultInteractionPolicies, DefaultInteractionPolicies,
InteractionPolicy, InteractionPolicy,
InteractionPolicyEntry, InteractionPolicyEntry,
InteractionPolicyValue, InteractionPolicyValue} from "../../../../lib/types/interaction";
import {
PolicyValueAuthor, PolicyValueAuthor,
PolicyValueFollowers, PolicyValueFollowers,
PolicyValueFollowing, PolicyValueFollowing,
@ -39,10 +40,11 @@ import {
} from "../../../../lib/types/interaction"; } from "../../../../lib/types/interaction";
import { useTextInput } from "../../../../lib/form"; import { useTextInput } from "../../../../lib/form";
import { Select } from "../../../../components/form/inputs"; import { Select } from "../../../../components/form/inputs";
import { TextFormInputHook } from "../../../../lib/form/types"; import type { TextFormInputHook } from "../../../../lib/form/types";
import { useBasicFor } from "./basic"; import { useBasicFor } from "./basic";
import { PolicyFormSomethingElse, useSomethingElseFor } from "./something-else"; import type { PolicyFormSomethingElse} from "./something-else";
import { Action, PolicyFormSub, SomethingElseValue, Visibility } from "./types"; import { useSomethingElseFor } from "./something-else";
import type { Action, PolicyFormSub, SomethingElseValue, Visibility } from "./types";
export default function InteractionPolicySettings() { export default function InteractionPolicySettings() {
const { const {

View File

@ -18,9 +18,10 @@
*/ */
import React, { useMemo } from "react"; import React, { useMemo } from "react";
import { InteractionPolicyValue, PolicyValueFollowers, PolicyValueFollowing, PolicyValuePublic } from "../../../../lib/types/interaction"; import type { InteractionPolicyValue} from "../../../../lib/types/interaction";
import { PolicyValueFollowers, PolicyValueFollowing, PolicyValuePublic } from "../../../../lib/types/interaction";
import { useTextInput } from "../../../../lib/form"; import { useTextInput } from "../../../../lib/form";
import { Action, Audience, PolicyFormSub, SomethingElseValue, Visibility } from "./types"; import type { Action, Audience, PolicyFormSub, SomethingElseValue, Visibility } from "./types";
export interface PolicyFormSomethingElse { export interface PolicyFormSomethingElse {
followers: PolicyFormSub, followers: PolicyFormSub,

View File

@ -17,8 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { TextFormInputHook } from "../../../../lib/form/types"; import type { TextFormInputHook } from "../../../../lib/form/types";
import React from "react"; import type React from "react";
export interface PolicyFormSub { export interface PolicyFormSub {
field: TextFormInputHook; field: TextFormInputHook;

View File

@ -34,18 +34,17 @@ import {
TextArea, TextArea,
FileInput, FileInput,
Checkbox, Checkbox,
Select Select,
} from "../../components/form/inputs"; } from "../../components/form/inputs";
import FormWithData from "../../lib/form/form-with-data"; import FormWithData from "../../lib/form/form-with-data";
import FakeProfile from "../../components/profile"; import FakeProfile from "../../components/profile";
import MutationButton from "../../components/form/mutation-button"; import MutationButton from "../../components/form/mutation-button";
import { useAccountThemesQuery } from "../../lib/query/user"; import { useAccountThemesQuery , useUpdateCredentialsMutation } from "../../lib/query/user";
import { useUpdateCredentialsMutation } from "../../lib/query/user";
import { useVerifyCredentialsQuery } from "../../lib/query/oauth"; import { useVerifyCredentialsQuery } from "../../lib/query/oauth";
import { useInstanceV1Query } from "../../lib/query/gts-api"; import { useInstanceV1Query } from "../../lib/query/gts-api";
import { Account } from "../../lib/types/account"; import type { Account } from "../../lib/types/account";
export default function UserProfile() { export default function UserProfile() {
return ( return (
@ -78,18 +77,18 @@ function UserProfileForm({ data: profile }: UserProfileFormProps) {
const { data: instance } = useInstanceV1Query(); const { data: instance } = useInstanceV1Query();
const instanceConfig = React.useMemo(() => { const instanceConfig = React.useMemo(() => {
return { return {
allowCustomCSS: instance?.configuration?.accounts?.allow_custom_css === true, allowCustomCSS: instance?.configuration.accounts.allow_custom_css === true,
maxPinnedFields: instance?.configuration?.accounts?.max_profile_fields ?? 6 maxPinnedFields: instance?.configuration.accounts.max_profile_fields ?? 6,
}; };
}, [instance]); }, [instance]);
// Parse out available theme options into nice format. // Parse out available theme options into nice format.
const { data: themes } = useAccountThemesQuery(); const { data: themes } = useAccountThemesQuery();
const themeOptions = useMemo(() => { const themeOptions = useMemo(() => {
let themeOptions = [ const themeOptions = [
<option key="" value=""> <option key="" value="">
Default Default
</option> </option>,
]; ];
themes?.forEach((theme) => { themes?.forEach((theme) => {
@ -101,7 +100,7 @@ function UserProfileForm({ data: profile }: UserProfileFormProps) {
themeOptions.push( themeOptions.push(
<option key={value} value={value}> <option key={value} value={value}>
{text} {text}
</option> </option>,
); );
}); });
@ -122,8 +121,8 @@ function UserProfileForm({ data: profile }: UserProfileFormProps) {
hideCollections: useBoolInput("hide_collections", { source: profile }), hideCollections: useBoolInput("hide_collections", { source: profile }),
webVisibility: useTextInput("web_visibility", { source: profile, valueSelector: (p) => p.source?.web_visibility }), webVisibility: useTextInput("web_visibility", { source: profile, valueSelector: (p) => p.source?.web_visibility }),
fields: useFieldArrayInput("fields_attributes", { fields: useFieldArrayInput("fields_attributes", {
defaultValue: profile?.source?.fields, defaultValue: profile.source?.fields,
length: instanceConfig.maxPinnedFields length: instanceConfig.maxPinnedFields,
}), }),
customCSS: useTextInput("custom_css", { source: profile, nosubmit: !instanceConfig.allowCustomCSS }), customCSS: useTextInput("custom_css", { source: profile, nosubmit: !instanceConfig.allowCustomCSS }),
theme: useTextInput("theme", { source: profile }), theme: useTextInput("theme", { source: profile }),
@ -134,7 +133,7 @@ function UserProfileForm({ data: profile }: UserProfileFormProps) {
onFinish: () => { onFinish: () => {
form.avatar.reset(); form.avatar.reset();
form.header.reset(); form.header.reset();
} },
}); });
const noAvatarSet = !profile.avatar_media_id; const noAvatarSet = !profile.avatar_media_id;
@ -322,7 +321,7 @@ function ProfileFields({ field: formField }) {
function Field({ index, data }) { function Field({ index, data }) {
const form = useWithFormContext(index, { const form = useWithFormContext(index, {
name: useTextInput("name", { defaultValue: data.name }), name: useTextInput("name", { defaultValue: data.name }),
value: useTextInput("value", { defaultValue: data.value }) value: useTextInput("value", { defaultValue: data.value }),
}); });
return ( return (