mirror of
1
Fork 0

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

View File

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

View File

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

View File

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

View File

@ -17,10 +17,9 @@
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 { Checkable, ChecklistInputHook } from "../lib/form/types";
import type { Checkable, ChecklistInputHook } from "../lib/form/types";
interface CheckListProps {
field: ChecklistInputHook;
@ -75,7 +74,7 @@ const CheckListEntries = memo(
getExtraProps={getExtraProps}
/>
));
}
},
);
interface CheckListEntryProps {
@ -94,7 +93,7 @@ const CheckListEntry = memo(
function CheckListEntry({ entry, updateValue, getExtraProps, EntryComponent }: CheckListEntryProps) {
const onChange = useCallback(
(value) => updateValue(entry.key, value),
[updateValue, entry.key]
[updateValue, entry.key],
);
const extraProps = useMemo(() => getExtraProps?.(entry), [getExtraProps, entry]);
@ -109,5 +108,5 @@ const CheckListEntry = memo(
<EntryComponent entry={entry} onChange={onChange} extraProps={extraProps} />
</label>
);
}
},
);

View File

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

View File

@ -21,7 +21,7 @@ import React from "react";
import { all } from "langs";
const asElements = all().map((l) => {
let code = l["1"].toUpperCase();
const code = l["1"].toUpperCase();
let name = l.name;
if (l.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/>.
*/
import React, { ReactNode } from "react";
import type { ReactNode } from "react";
import React from "react";
import { useLocation } from "wouter";
import { Error } from "./error";
import { SerializedError } from "@reduxjs/toolkit";
import { FetchBaseQueryError } from "@reduxjs/toolkit/query";
import { Links } from "parse-link-header";
import type { SerializedError } from "@reduxjs/toolkit";
import type { FetchBaseQueryError } from "@reduxjs/toolkit/query";
import type { Links } from "parse-link-header";
import Loading from "./loading";
export interface PageableListProps<T> {

View File

@ -19,14 +19,14 @@
import React from "react";
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";
export function FakeStatus({ children }) {
const { data: account = {
avatar: "/assets/default_avatars/GoToSocial_icon1.webp",
display_name: "",
username: ""
username: "",
} } = useVerifyCredentialsQuery();
return (

View File

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

View File

@ -26,7 +26,7 @@ import { PersistGate } from "redux-persist/integration/react";
import { store, persistor } from "./redux/store";
import { Authorization } from "./components/authorization";
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 { SidebarMenu } from "./lib/navigation/menu";
import { Redirect, Route, Router } from "wouter";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,9 +17,8 @@
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 type {
@ -35,8 +34,8 @@ export default function useFileInput(
{
withPreview,
maxSize,
initialInfo = "no file selected"
}: HookOpts<File>
initialInfo = "no file selected",
}: HookOpts<File>,
): FileFormInputHook {
const [file, setFile] = useState<File>();
const [imageURL, setImageURL] = useState<string>();
@ -58,7 +57,7 @@ export default function useFileInput(
return;
}
let file = files[0];
const file = files[0];
setFile(file);
if (imageURL) {
@ -76,7 +75,7 @@ export default function useFileInput(
<ErrorC
error={new Error(`file size ${sizePrettier} is larger than max size ${maxSizePrettier}`)}
reset={(reset)}
/>
/>,
);
} else {
setInfo(<>{file.name} ({sizePrettier})</>);
@ -100,7 +99,7 @@ export default function useFileInput(
[name]: file,
[`${name}URL`]: imageURL,
[`${name}Info`]: infoComponent,
}
},
], {
onChange,
reset,

View File

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

View File

@ -17,17 +17,17 @@
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(
form: HookedForm,
{ changedOnly }: { changedOnly: boolean },
): {
updatedFields: FormInputHook<any>[];
mutationData: {
[k: string]: any;
};
} {
updatedFields: FormInputHook[];
mutationData: {
[k: string]: any;
};
} {
const updatedFields: FormInputHook[] = [];
const mutationData: Array<[string, any]> = [];

View File

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

View File

@ -60,7 +60,7 @@ interface UseFormSubmitOptions {
export default function useFormSubmit(
form: HookedForm,
mutationQuery: readonly [MutationTrigger<any>, UseMutationStateResult<any, any>],
opts: UseFormSubmitOptions = { changedOnly: true }
opts: UseFormSubmitOptions = { changedOnly: true },
): [ FormSubmitFunction, FormSubmitResult ] {
if (!Array.isArray(mutationQuery)) {
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.
//
// 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 {
// No submitter defined. Fall back
// to just use the FormSubmitEvent.
@ -125,7 +125,7 @@ export default function useFormSubmit(
onFinish(res);
}
} catch (e) {
// eslint-disable-next-line no-console
console.error(`caught error running mutation: ${e}`);
}
};
@ -134,7 +134,7 @@ export default function useFormSubmit(
submitForm,
{
...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/>.
*/
import React, {
import type React from "react";
import {
useState,
useRef,
useTransition,
@ -41,7 +42,7 @@ export default function useTextInput(
showValidation = true,
initValidation,
nosubmit = false,
}: HookOpts<string>
}: HookOpts<string>,
): TextFormInputHook {
const [text, setText] = useState(initialValue);
const textRef = useRef<HTMLInputElement>(null);
@ -86,7 +87,7 @@ export default function useTextInput(
[`${name}Ref`]: textRef,
[`set${Name}`]: setText,
[`${name}Valid`]: valid,
}
},
], {
onChange,
reset,
@ -97,8 +98,10 @@ export default function useTextInput(
ref: textRef,
setter: setText,
valid,
validate: () => setValidation(validator ? validator(text): ""),
validate: () => {
setValidation(validator ? validator(text): "");
},
hasChanged: () => text != initialValue,
_default
_default,
});
}

View File

@ -17,12 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* eslint-disable no-unused-vars */
import { ComboboxState } from "ariakit";
import React from "react";
import {
import type { ComboboxState } from "ariakit";
import type React, {
ChangeEventHandler,
Dispatch,
RefObject,
@ -30,6 +26,7 @@ import {
SyntheticEvent,
} from "react";
export interface CreateHookNames {
name: string;
Name: string;
@ -225,9 +222,9 @@ export interface ChecklistInputHook<T = Checkable> extends FormInputHook<{[k: st
_withSelectedFieldValues,
_withSomeSelected,
_withUpdateMultiple {
// Uses its own funky onChange handler.
onChange: (key: any, value: any) => void
}
// Uses its own funky onChange handler.
onChange: (key: any, value: any) => void
}
export type AnyFormInputHook =
FormInputHook |

View File

@ -17,7 +17,8 @@
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 {
@ -48,7 +49,7 @@ class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
componentDidCatch(_e, info) {
this.setState({
...this.state,
componentStack: info.componentStack
componentStack: info.componentStack,
});
}
@ -83,7 +84,7 @@ function ErrorFallback({ error, componentStack, resetErrorBoundary }) {
{componentStack && [
"\n\nComponent trace:",
componentStack
componentStack,
]}
{["\n\nError trace: ", error.stack]}
</pre>

View File

@ -17,7 +17,8 @@
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 {
BaseUrlContext,

View File

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

View File

@ -18,8 +18,8 @@
*/
import { gtsApi } from "../../gts-api";
import { FetchBaseQueryError } from "@reduxjs/toolkit/query";
import { RootState } from "../../../../redux/store";
import type { FetchBaseQueryError } from "@reduxjs/toolkit/query";
import type { RootState } from "../../../../redux/store";
import type { CustomEmoji, EmojisFromItem, ListEmojiParams } from "../../../types/custom-emoji";
@ -77,39 +77,39 @@ const extended = gtsApi.injectEndpoints({
url: "/api/v1/admin/custom_emojis",
params: {
limit: 0,
...params
}
...params,
},
}),
providesTags: (res, _error, _arg) =>
res
? [
...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>({
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) => {
return {
method: "POST",
url: `/api/v1/admin/custom_emojis`,
asForm: true,
body: form,
discardEmpty: true
discardEmpty: true,
};
},
invalidatesTags: (res) =>
res
? [{ type: "Emoji", id: "LIST" }, { type: "Emoji", id: res.id }]
: [{ type: "Emoji", id: "LIST" }]
: [{ type: "Emoji", id: "LIST" }],
}),
editEmoji: build.mutation<CustomEmoji, any>({
@ -120,22 +120,22 @@ const extended = gtsApi.injectEndpoints({
asForm: true,
body: {
type: "modify",
...patch
}
...patch,
},
};
},
invalidatesTags: (res) =>
res
? [{ type: "Emoji", id: "LIST" }, { type: "Emoji", id: res.id }]
: [{ type: "Emoji", id: "LIST" }]
: [{ type: "Emoji", id: "LIST" }],
}),
deleteEmoji: build.mutation<any, string>({
query: (id) => ({
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>({
@ -145,10 +145,10 @@ const extended = gtsApi.injectEndpoints({
// First search for given url.
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) {
return { error: searchRes.error as FetchBaseQueryError };
return { error: searchRes.error };
}
// Parse initial results of search.
@ -178,8 +178,8 @@ const extended = gtsApi.injectEndpoints({
url: `/api/v1/admin/custom_emojis`,
params: {
filter: `domain:${domain},shortcode:${emoji.shortcode}`,
limit: 1
}
limit: 1,
},
});
if (emojiRes.error) {
@ -191,7 +191,7 @@ const extended = gtsApi.injectEndpoints({
// Got it!
return emojiRes.data as CustomEmoji;
})
}),
)
).flatMap((emoji) => {
// Remove any nulls.
@ -205,7 +205,7 @@ const extended = gtsApi.injectEndpoints({
status: 400,
statusText: 'Bad Request',
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,
domain,
list: withIDs,
}
},
};
}
},
}),
patchRemoteEmojis: build.mutation({
@ -231,7 +231,7 @@ const extended = gtsApi.injectEndpoints({
// Map function to get a promise
// of an emoji (or null).
const copyEmoji = async(emoji: CustomEmoji) => {
let body: {
const body: {
type: string;
shortcode?: string;
category?: string;
@ -285,7 +285,7 @@ const extended = gtsApi.injectEndpoints({
status: 400,
statusText: 'Bad Request',
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 };
},
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/>.
*/
import { ApURLResponse } from "../../../types/debug";
import type { ApURLResponse } from "../../../types/debug";
import { gtsApi } from "../../gts-api";
const extended = gtsApi.injectEndpoints({
@ -32,13 +32,13 @@ const extended = gtsApi.injectEndpoints({
return {
url: `/api/v1/admin/debug/apurl?${urlParam.toString()}`,
};
}
},
}),
ClearCaches: build.mutation<{}, void>({
query: () => ({
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 { gtsApi } from "../../gts-api";
import { RootState } from "../../../../redux/store";
import { FetchBaseQueryError } from "@reduxjs/toolkit/query";
import { DomainPerm, ExportDomainPermsParams } from "../../../types/domain-permission";
import type { RootState } from "../../../../redux/store";
import type { FetchBaseQueryError } from "@reduxjs/toolkit/query";
import type { DomainPerm, ExportDomainPermsParams } from "../../../types/domain-permission";
interface _exportProcess {
transformEntry: (_entry: DomainPerm) => any;
@ -45,11 +45,11 @@ function exportProcess(formData: ExportDomainPermsParams): _exportProcess {
transformEntry: (entry) => ({
domain: entry.domain,
public_comment: entry.public_comment,
obfuscate: entry.obfuscate
obfuscate: entry.obfuscate,
}),
stringify: (list) => JSON.stringify(list),
extension: ".json",
mime: "application/json"
mime: "application/json",
};
}
@ -61,7 +61,7 @@ function exportProcess(formData: ExportDomainPermsParams): _exportProcess {
false, // reject_media
false, // reject_reports
entry.public_comment ?? "", // public_comment
entry.obfuscate ?? false // obfuscate
entry.obfuscate ?? false, // obfuscate
],
stringify: (list) => csvUnparse({
fields: [
@ -72,10 +72,10 @@ function exportProcess(formData: ExportDomainPermsParams): _exportProcess {
"#public_comment",
"#obfuscate",
],
data: list
data: list,
}),
extension: ".csv",
mime: "text/csv"
mime: "text/csv",
};
}
@ -84,7 +84,7 @@ function exportProcess(formData: ExportDomainPermsParams): _exportProcess {
transformEntry: (entry) => entry.domain,
stringify: (list) => list.join("\n"),
extension: ".txt",
mime: "text/plain"
mime: "text/plain",
};
}
@ -98,7 +98,7 @@ const extended = gtsApi.injectEndpoints({
// we want the untransformed array version.
const permsRes = await fetchWithBQ({ url: `/api/v1/admin/domain_${formData.permType}s` });
if (permsRes.error) {
return { error: permsRes.error as FetchBaseQueryError };
return { error: permsRes.error };
}
// Process perms into desired export format.
@ -130,16 +130,16 @@ const extended = gtsApi.injectEndpoints({
fileDownload(
exportAsString,
filename + process.extension,
process.mime
process.mime,
);
// js-file-download handles the
// nitty gritty for us, so we can
// just return null data.
return { data: null };
}
},
}),
})
}),
});
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -44,7 +44,7 @@ const extended = gtsApi.injectEndpoints({
}
return {
url: `/api/v1/admin/reports${query}`
url: `/api/v1/admin/reports${query}`,
};
},
// Headers required for paging.
@ -56,15 +56,15 @@ const extended = gtsApi.injectEndpoints({
},
// Only provide LIST tag id since this model is not the
// same as getReport model (due to transformResponse).
providesTags: [{ type: "Report", id: "TRANSFORMED" }]
providesTags: [{ type: "Report", id: "TRANSFORMED" }],
}),
getReport: build.query<AdminReport, string>({
query: (id) => ({
url: `/api/v1/admin/reports/${id}`
url: `/api/v1/admin/reports/${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`,
method: "POST",
asForm: true,
body: formData
body: formData,
}),
invalidatesTags: (res) =>
res
? [{ 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 type { FetchBaseQueryMeta } from "@reduxjs/toolkit/dist/query/fetchBaseQuery";
import type { RootState } from '../../redux/store';
import { InstanceV1 } from '../types/instance';
import type { InstanceV1 } from '../types/instance';
/**
* GTSFetchArgs extends standard FetchArgs used by
@ -173,10 +173,10 @@ export const gtsApi = createApi({
endpoints: (build) => ({
instanceV1: build.query<InstanceV1, void>({
query: () => ({
url: `/api/v1/instance`
})
})
})
url: `/api/v1/instance`,
}),
}),
}),
});
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,80 +18,80 @@
*/
export interface InstanceV1 {
uri: string;
account_domain: string;
title: string;
description: string;
description_text?: string;
short_description: string;
short_description_text?: string;
email: string;
version: string;
debug?: boolean;
languages: any[]; // TODO: define this
registrations: boolean;
approval_required: boolean;
invites_enabled: boolean;
configuration: InstanceConfiguration;
urls: InstanceUrls;
stats: InstanceStats;
thumbnail: string;
contact_account: Object; // TODO: define this.
max_toot_chars: number;
rules: any[]; // TODO: define this
terms?: string;
terms_text?: string;
uri: string;
account_domain: string;
title: string;
description: string;
description_text?: string;
short_description: string;
short_description_text?: string;
email: string;
version: string;
debug?: boolean;
languages: any[]; // TODO: define this
registrations: boolean;
approval_required: boolean;
invites_enabled: boolean;
configuration: InstanceConfiguration;
urls: InstanceUrls;
stats: InstanceStats;
thumbnail: string;
contact_account: object; // TODO: define this.
max_toot_chars: number;
rules: any[]; // TODO: define this
terms?: string;
terms_text?: string;
}
export interface InstanceConfiguration {
statuses: InstanceStatuses;
media_attachments: InstanceMediaAttachments;
polls: InstancePolls;
accounts: InstanceAccounts;
emojis: InstanceEmojis;
oidc_enabled?: boolean;
statuses: InstanceStatuses;
media_attachments: InstanceMediaAttachments;
polls: InstancePolls;
accounts: InstanceAccounts;
emojis: InstanceEmojis;
oidc_enabled?: boolean;
}
export interface InstanceAccounts {
allow_custom_css: boolean;
max_featured_tags: number;
max_profile_fields: number;
allow_custom_css: boolean;
max_featured_tags: number;
max_profile_fields: number;
}
export interface InstanceEmojis {
emoji_size_limit: number;
emoji_size_limit: number;
}
export interface InstanceMediaAttachments {
supported_mime_types: string[];
image_size_limit: number;
image_matrix_limit: number;
video_size_limit: number;
video_frame_rate_limit: number;
video_matrix_limit: number;
supported_mime_types: string[];
image_size_limit: number;
image_matrix_limit: number;
video_size_limit: number;
video_frame_rate_limit: number;
video_matrix_limit: number;
}
export interface InstancePolls {
max_options: number;
max_characters_per_option: number;
min_expiration: number;
max_expiration: number;
max_options: number;
max_characters_per_option: number;
min_expiration: number;
max_expiration: number;
}
export interface InstanceStatuses {
max_characters: number;
max_media_attachments: number;
characters_reserved_per_url: number;
supported_mime_types: string[];
max_characters: number;
max_media_attachments: number;
characters_reserved_per_url: number;
supported_mime_types: string[];
}
export interface InstanceStats {
domain_count: number;
status_count: number;
user_count: number;
domain_count: number;
status_count: number;
user_count: number;
}
export interface InstanceUrls {
streaming_api: string;
streaming_api: string;
}

View File

@ -17,9 +17,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Links } from "parse-link-header";
import { Account } from "./account";
import { Status } from "./status";
import type { Links } from "parse-link-header";
import type { Account } from "./account";
import type { Status } from "./status";
export interface DefaultInteractionPolicies {
direct: InteractionPolicy;
@ -71,7 +71,7 @@ export {
* Interaction request targeting a status by an account.
*/
export interface InteractionRequest {
/**
/**
* ID of the request.
*/
id: string;

View File

@ -17,7 +17,7 @@
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
@ -46,25 +46,25 @@ interface MutationStartedParams {
/**
* A method to get the current state for the store.
*/
getState,
getState,
/**
* extra as provided as thunk.extraArgument to the configureStore getDefaultMiddleware option.
*/
extra,
extra,
/**
* A unique ID generated for the query/mutation.
*/
requestId,
requestId,
/**
* A Promise that will resolve with a data property (the transformed query result), and a
* meta property (meta returned by the baseQuery). If the query fails, this Promise will
* reject with the error. This allows you to await for the query to finish.
*/
queryFulfilled,
queryFulfilled,
/**
* A function that gets the current value of the cache entry.
*/
getCacheEntry,
getCacheEntry,
}
export type Action = (

View File

@ -17,16 +17,16 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Links } from "parse-link-header";
import { AdminAccount } from "./account";
import { Status } from "./status";
import type { Links } from "parse-link-header";
import type { AdminAccount } from "./account";
import type { Status } from "./status";
/**
* Admin model of a report. Differs from the client
* model, which contains less detailed information.
*/
export interface AdminReport {
/**
/**
* ID of the report.
*/
id: string;
@ -83,7 +83,7 @@ export interface AdminReport {
* Rules broken according to the reporter, if any.
* TODO: model this properly.
*/
rules: Object[];
rules: object[];
/**
* Comment stored about what action (if any) was taken.
*/
@ -94,7 +94,7 @@ export interface AdminReport {
* Parameters for POST to /api/v1/admin/reports/{id}/resolve.
*/
export interface AdminReportResolveParams {
/**
/**
* The ID of the report to resolve.
*/
id: string;

View File

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

View File

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

View File

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

View File

@ -17,7 +17,8 @@
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 { useReducer } from "react";
@ -57,12 +58,12 @@ function initialHookState({
}
return [ key, { ...entry, key, checked } ];
})
}),
);
return {
entries: mappedEntries,
selectedEntries
selectedEntries,
};
}
@ -81,7 +82,7 @@ const checklistSlice = createSlice({
}
return [entry.key, { ...entry, checked } ];
})
}),
);
return { entries, selectedEntries };
@ -97,7 +98,7 @@ const checklistSlice = createSlice({
state.entries[key] = {
...state.entries[key],
...value
...value,
};
},
updateMultiple: (state, { payload }: PayloadAction<Array<[key: string, value: Partial<Checkable>]>>) => {
@ -112,11 +113,11 @@ const checklistSlice = createSlice({
state.entries[key] = {
...state.entries[key],
...value
...value,
};
});
}
}
},
},
});
export const actions = checklistSlice.actions;
@ -153,6 +154,6 @@ export const useChecklistReducer = (entries: Checkable[], uniqueKey: string, ini
return useReducer(
checklistSlice.reducer,
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/>.
*/
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
import type { PayloadAction} from "@reduxjs/toolkit";
import { createSlice } from "@reduxjs/toolkit";
/**
* OAuthToken represents a response
@ -78,8 +79,8 @@ export const oauthSlice = createSlice({
delete state.token;
delete state.app;
state.loginState = "logout";
}
}
},
},
});
export const {

View File

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

View File

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

View File

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

View File

@ -22,7 +22,7 @@ import { useTextInput } from "../../../../lib/form";
import { useLazyApURLQuery } from "../../../../lib/query/admin/debug";
import { TextInput } from "../../../../components/form/inputs";
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";
// 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/>.
*/
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 ComboBox from "../../../components/combo-box";
import { useListEmojiQuery } from "../../../lib/query/admin/custom-emoji";
import { CustomEmoji } from "../../../lib/types/custom-emoji";
import { ComboboxFormInputHook } from "../../../lib/form/types";
import type { CustomEmoji } from "../../../lib/types/custom-emoji";
import type { ComboboxFormInputHook } from "../../../lib/form/types";
import Loading from "../../../components/loading";
import { Error } from "../../../components/error";
@ -97,7 +98,7 @@ export function CategorySelect({ field, children }: PropsWithChildren<CategorySe
aria-hidden="true"
/>
{categoryName}
</>
</>,
]);
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,7 +23,7 @@ import { useTextInput } from "../../../lib/form";
import useFormSubmit from "../../../lib/form/submit";
import { TextInput } from "../../../components/form/inputs";
import MutationButton from "../../../components/form/mutation-button";
import { PermType } from "../../../lib/types/perm";
import type { PermType } from "../../../lib/types/perm";
import { RE2JS } from "re2js";
export default function HeaderPermCreateForm({ permType }: { permType: PermType }) {
@ -44,7 +44,7 @@ export default function HeaderPermCreateForm({ permType }: { permType: PermType
}
return "";
}
},
}),
regex: useTextInput("regex", {
validator: (val: string) => {
@ -63,7 +63,7 @@ export default function HeaderPermCreateForm({ permType }: { permType: PermType
}
return "";
}
},
}),
};
@ -101,7 +101,7 @@ export default function HeaderPermCreateForm({ permType }: { permType: PermType
label={
<>
Header Name&nbsp;
<a
<a
href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers"
target="_blank"
className="docslink"

View File

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

View File

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

View File

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

View File

@ -25,7 +25,7 @@ import { useTextInput } from "../../../lib/form";
import useFormSubmit from "../../../lib/form/submit";
import { TextArea } from "../../../components/form/inputs";
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";
export default function InstanceRules() {
@ -46,7 +46,9 @@ function InstanceRulesForm({ data: rules }: { data: MappedRules }) {
const [submitForm, result] = useFormSubmit({ newRule }, useAddInstanceRuleMutation(), {
changedOnly: true,
onFinish: () => newRule.reset()
onFinish: () => {
newRule.reset();
},
});
return (

View File

@ -24,7 +24,7 @@ import { TextInput, TextArea, FileInput } from "../../../components/form/inputs"
import MutationButton from "../../../components/form/mutation-button";
import { useInstanceV1Query } from "../../../lib/query/gts-api";
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 useFormSubmit from "../../../lib/form/submit";
@ -50,7 +50,7 @@ function InstanceSettingsForm({ data: instance }: InstanceSettingsFormProps) {
const form = {
title: useTextInput("title", {
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 }),
thumbnailDesc: useTextInput("thumbnail_description", { source: instance }),
@ -58,22 +58,22 @@ function InstanceSettingsForm({ data: instance }: InstanceSettingsFormProps) {
source: instance,
// Select "raw" text version of parsed field for editing.
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", {
source: instance,
// Select "raw" text version of parsed field for editing.
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", {
source: instance,
// Select "raw" text version of parsed field for editing.
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 }),
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());
@ -109,8 +109,8 @@ function InstanceSettingsForm({ data: instance }: InstanceSettingsFormProps) {
<div className="file-upload-with-preview">
<img
className="preview avatar"
src={form.thumbnail.previewValue ?? instance?.thumbnail}
alt={form.thumbnailDesc.value ?? (instance?.thumbnail ? `Thumbnail image for the instance` : "No instance thumbnail image set")}
src={form.thumbnail.previewValue ?? instance.thumbnail}
alt={form.thumbnailDesc.value ?? (instance.thumbnail ? `Thumbnail image for the instance` : "No instance thumbnail image set")}
/>
<div className="file-input-with-image-description">
<FileInput

View File

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

View File

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

View File

@ -17,12 +17,13 @@
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 { PageableList } from "../../../../components/pageable-list";
import { useLocation } from "wouter";
import Username from "../../../../components/username";
import { AdminAccount } from "../../../../lib/types/account";
import type { AdminAccount } from "../../../../lib/types/account";
export default function AccountsPending() {
const [ location, _setLocation ] = useLocation();

View File

@ -17,7 +17,8 @@
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 { useTextInput } from "../../../../lib/form";
@ -25,7 +26,7 @@ import { PageableList } from "../../../../components/pageable-list";
import { Select, TextInput } from "../../../../components/form/inputs";
import MutationButton from "../../../../components/form/mutation-button";
import { useLocation, useSearch } from "wouter";
import { AdminAccount } from "../../../../lib/types/account";
import type { AdminAccount } from "../../../../lib/types/account";
import Username from "../../../../components/username";
import isValidDomain from "is-valid-domain";
@ -66,11 +67,11 @@ export function AccountSearchForm() {
}
return "invalid domain";
}
},
}),
email: useTextInput("email", { defaultValue: urlQueryParams.get("email") ?? ""}),
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,

View File

@ -17,9 +17,8 @@
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 { 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 { 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 { Error } from "../../../components/error";
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";
export default function DomainPermDetail() {
const baseUrl = useBaseUrl();
// Parse perm type from routing params.
let params = useParams();
const params = useParams();
if (params.permType !== "blocks" && params.permType !== "allows") {
throw "unrecognized perm type " + params.permType;
}
@ -132,7 +131,7 @@ function DomainPermForm({ defaultDomain, perm, permType }: DomainPermFormProps)
const disabledForm = isExistingPerm
? {
disabled: true,
title: "Domain permissions currently cannot be edited."
title: "Domain permissions currently cannot be edited.",
}
: {
disabled: false,
@ -164,11 +163,11 @@ function DomainPermForm({ defaultDomain, perm, permType }: DomainPermFormProps)
}
return "invalid domain";
}
},
}),
obfuscate: useBoolInput("obfuscate", { 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
@ -221,11 +220,11 @@ function DomainPermForm({ defaultDomain, perm, permType }: DomainPermFormProps)
// but if domain input changes, that doesn't match anymore
// and causes issues later on so, before submitting the form,
// silently change url, and THEN submit.
let correctUrl = `/${permType}s/${form.domain.value}`;
const correctUrl = `/${permType}s/${form.domain.value}`;
if (location != correctUrl) {
setLocation(correctUrl);
}
return submitForm(e);
submitForm(e);
}
return (

View File

@ -17,9 +17,8 @@
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 useFormSubmit from "../../../lib/form/submit";
import {
@ -96,7 +95,9 @@ export default function ImportExportForm({ form, submitParse, parseResult }: Imp
<MutationButton
label="Import"
type="button"
onClick={() => submitParse()}
onClick={() => {
submitParse();
}}
result={parseResult}
showError={false}
disabled={form.permType.value === undefined || form.permType.value.length === 0}
@ -116,7 +117,9 @@ export default function ImportExportForm({ form, submitParse, parseResult }: Imp
<MutationButton
label="Export"
type="button"
onClick={() => submitExport("export")}
onClick={() => {
submitExport("export");
}}
result={exportResult} showError={false}
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"
wrapperClassName="export-file-button"
type="button"
onClick={() => submitExport("export-file")}
onClick={() => {
submitExport("export-file");
}}
result={exportResult}
showError={false}
disabled={form.permType.value === undefined || form.permType.value.length === 0}

View File

@ -37,8 +37,8 @@ export default function ImportExport() {
options: {
block: "Domain blocks",
allow: "Domain allows",
}
})
},
}),
};
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/>.
*/
import React from "react";
import React, { useMemo } from "react";
import { useMemo } from "react";
import { Link, useLocation, useParams } from "wouter";
import { matchSorter } from "match-sorter";
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 type { MappedDomainPerms } from "../../../lib/types/domain-permission";
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";
export default function DomainPermissionsOverview() {
const baseUrl = useBaseUrl();
// Parse perm type from routing params.
let params = useParams();
const params = useParams();
if (params.permType !== "blocks" && params.permType !== "allows") {
throw "unrecognized perm type " + params.permType;
}

View File

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

View File

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

View File

@ -17,7 +17,8 @@
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 { useTextInput } from "../../../lib/form";
@ -26,7 +27,7 @@ import { Select } from "../../../components/form/inputs";
import MutationButton from "../../../components/form/mutation-button";
import { useLocation, useSearch } from "wouter";
import Username from "../../../components/username";
import { AdminReport } from "../../../lib/types/report";
import type { AdminReport } from "../../../lib/types/report";
export default function ReportsSearch() {
return (
@ -61,7 +62,7 @@ function ReportSearchForm() {
resolved: useTextInput("resolved", { defaultValue: resolved }),
account_id: useTextInput("account_id", { defaultValue: urlQueryParams.get("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;
@ -196,7 +197,7 @@ function ReportListEntry({ report, linkTo, backLocation }: ReportEntryProps) {
// Store the back location in history so
// the detail view can use it to return to
// this page (including query parameters).
state: { backLocation: backLocation }
state: { backLocation: backLocation },
});
}}
role="link"

View File

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

View File

@ -28,7 +28,7 @@ import {
import MutationButton from "../../../components/form/mutation-button";
import useFormSubmit from "../../../lib/form/submit";
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 }) {
const [exportFollowing, exportFollowingResult] = useFormSubmit(
@ -92,7 +92,7 @@ export default function Export({ exportStats }: { exportStats: AccountExportStat
className="docslink"
rel="noreferrer"
>
Learn more about this section (opens in a new tab)
Learn more about this section (opens in a new tab)
</a>
</div>
@ -105,7 +105,9 @@ export default function Export({ exportStats }: { exportStats: AccountExportStat
className="text-cutoff"
label="Download following.csv"
type="button"
onClick={() => exportFollowing()}
onClick={() => {
exportFollowing();
}}
result={exportFollowingResult}
showError={true}
disabled={exportStats.following_count === 0}
@ -119,7 +121,9 @@ export default function Export({ exportStats }: { exportStats: AccountExportStat
className="text-cutoff"
label="Download followers.csv"
type="button"
onClick={() => exportFollowers()}
onClick={() => {
exportFollowers();
}}
result={exportFollowersResult}
showError={true}
disabled={exportStats.followers_count === 0}
@ -133,7 +137,9 @@ export default function Export({ exportStats }: { exportStats: AccountExportStat
className="text-cutoff"
label="Download lists.csv"
type="button"
onClick={() => exportLists()}
onClick={() => {
exportLists();
}}
result={exportListsResult}
showError={true}
disabled={exportStats.lists_count === 0}
@ -147,7 +153,9 @@ export default function Export({ exportStats }: { exportStats: AccountExportStat
className="text-cutoff"
label="Download blocks.csv"
type="button"
onClick={() => exportBlocks()}
onClick={() => {
exportBlocks();
}}
result={exportBlocksResult}
showError={true}
disabled={exportStats.blocks_count === 0}
@ -161,7 +169,9 @@ export default function Export({ exportStats }: { exportStats: AccountExportStat
className="text-cutoff"
label="Download mutes.csv"
type="button"
onClick={() => exportMutes()}
onClick={() => {
exportMutes();
}}
result={exportMutesResult}
showError={true}
disabled={exportStats.mutes_count === 0}

View File

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

View File

@ -23,7 +23,7 @@ import FormWithData from "../../../lib/form/form-with-data";
import BackButton from "../../../components/back-button";
import { useBaseUrl } from "../../../lib/navigation/util";
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 MutationButton from "../../../components/form/mutation-button";
import { Status } from "../../../components/status";
@ -31,7 +31,7 @@ import { Status } from "../../../components/status";
export default function InteractionRequestDetail({ }) {
const params: { reqId: string } = useParams();
const baseUrl = useBaseUrl();
const backLocation: String = history.state?.backLocation ?? `~${baseUrl}`;
const backLocation: string = history.state?.backLocation ?? `~${baseUrl}`;
return (
<div className="interaction-request-detail">

View File

@ -17,14 +17,15 @@
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 { PageableList } from "../../../components/pageable-list";
import MutationButton from "../../../components/form/mutation-button";
import { useLocation, useSearch } from "wouter";
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 { useContent, useIcon, useNoun, useVerbed } from "./util";
@ -46,16 +47,16 @@ export default function InteractionRequestsSearchForm() {
// urlQueryParams, to allow paging.
const form = {
statusID: useTextInput("status_id", {
defaultValue: urlQueryParams.get("status_id") ?? ""
defaultValue: urlQueryParams.get("status_id") ?? "",
}),
likes: useBoolInput("favourites", {
defaultValue: defaultTrue(urlQueryParams.get("favourites"))
defaultValue: defaultTrue(urlQueryParams.get("favourites")),
}),
replies: useBoolInput("replies", {
defaultValue: defaultTrue(urlQueryParams.get("replies"))
defaultValue: defaultTrue(urlQueryParams.get("replies")),
}),
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
// the detail view can use it to return to
// this page (including query parameters).
state: { backLocation: backLocation }
state: { backLocation: backLocation },
});
}}
role="link"

View File

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

View File

@ -142,8 +142,8 @@ function AlsoKnownAsURI({ index, data }) {
}
function MoveForm({ data: profile }) {
let urlStr = store.getState().oauth.instanceUrl ?? "";
let url = new URL(urlStr);
const urlStr = store.getState().oauth.instanceUrl ?? "";
const url = new URL(urlStr);
const form = {
movedToURI: useTextInput("moved_to_uri", {
@ -162,10 +162,10 @@ function MoveForm({ data: profile }) {
<div className="form-section-docs">
<h3>Move Account</h3>
<p>
For a move to be successful, you must have already set an alias from the
target account back to the account you're moving from (ie., this account),
using the settings panel of the instance on which the target account resides.
To do this, provide the following details to the other instance:
For a move to be successful, you must have already set an alias from the
target account back to the account you're moving from (ie., this account),
using the settings panel of the instance on which the target account resides.
To do this, provide the following details to the other instance:
</p>
<dl className="migration-details">
<div>

View File

@ -24,7 +24,7 @@ import { Select, Checkbox } from "../../../../components/form/inputs";
import Languages from "../../../../components/languages";
import MutationButton from "../../../../components/form/mutation-button";
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 }) {
/* form keys
@ -36,7 +36,7 @@ export default function BasicSettings({ account }: { account: Account }) {
const form = {
defaultPrivacy: useTextInput("source[privacy]", { source: account, defaultValue: "unlisted" }),
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" }),
};
@ -52,7 +52,7 @@ export default function BasicSettings({ account }: { account: Account }) {
className="docslink"
rel="noreferrer"
>
Learn more about these settings (opens in a new tab)
Learn more about these settings (opens in a new tab)
</a>
</div>
<Select field={form.language} label="Default post language" options={

View File

@ -18,15 +18,16 @@
*/
import React, { useMemo } from "react";
import type {
InteractionPolicyValue} from "../../../../lib/types/interaction";
import {
InteractionPolicyValue,
PolicyValueAuthor,
PolicyValueFollowers,
PolicyValueMentioned,
PolicyValuePublic,
} from "../../../../lib/types/interaction";
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,
// derives what the initial basic Select value should be.

View File

@ -26,11 +26,12 @@ import {
import Loading from "../../../../components/loading";
import { Error } from "../../../../components/error";
import MutationButton from "../../../../components/form/mutation-button";
import {
import type {
DefaultInteractionPolicies,
InteractionPolicy,
InteractionPolicyEntry,
InteractionPolicyValue,
InteractionPolicyValue} from "../../../../lib/types/interaction";
import {
PolicyValueAuthor,
PolicyValueFollowers,
PolicyValueFollowing,
@ -39,10 +40,11 @@ import {
} from "../../../../lib/types/interaction";
import { useTextInput } from "../../../../lib/form";
import { Select } from "../../../../components/form/inputs";
import { TextFormInputHook } from "../../../../lib/form/types";
import type { TextFormInputHook } from "../../../../lib/form/types";
import { useBasicFor } from "./basic";
import { PolicyFormSomethingElse, useSomethingElseFor } from "./something-else";
import { Action, PolicyFormSub, SomethingElseValue, Visibility } from "./types";
import type { PolicyFormSomethingElse} from "./something-else";
import { useSomethingElseFor } from "./something-else";
import type { Action, PolicyFormSub, SomethingElseValue, Visibility } from "./types";
export default function InteractionPolicySettings() {
const {
@ -134,7 +136,7 @@ function InteractionPoliciesForm({ defaultPolicies }: InteractionPoliciesFormPro
these settings; they do not apply retroactively.
<br/>
The word "anyone" in the below options means <em>anyone with
permission to see the post</em>, taking account of blocks.
permission to see the post</em>, taking account of blocks.
<br/>
Bear in mind that no matter what you set below, you will always
be able to like, reply-to, and boost your own posts.

View File

@ -18,9 +18,10 @@
*/
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 { Action, Audience, PolicyFormSub, SomethingElseValue, Visibility } from "./types";
import type { Action, Audience, PolicyFormSub, SomethingElseValue, Visibility } from "./types";
export interface PolicyFormSomethingElse {
followers: PolicyFormSub,

View File

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

View File

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