mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-05-20 09:18:13 +00:00
Compare commits
4 commits
c5a0a53e62
...
24c616f0ee
Author | SHA1 | Date | |
---|---|---|---|
24c616f0ee | |||
713c32100e | |||
f456bd3401 | |||
9a66fa0ef6 |
6
internal/cache/db.go
vendored
6
internal/cache/db.go
vendored
|
@ -148,15 +148,15 @@ type GTSCaches struct {
|
|||
// Tag provides access to the gtsmodel Tag database cache.
|
||||
Tag StructCache[*gtsmodel.Tag]
|
||||
|
||||
// ThreadMute provides access to the gtsmodel ThreadMute database cache.
|
||||
ThreadMute StructCache[*gtsmodel.ThreadMute]
|
||||
|
||||
// Token provides access to the gtsmodel Token database cache.
|
||||
Token StructCache[*gtsmodel.Token]
|
||||
|
||||
// Tombstone provides access to the gtsmodel Tombstone database cache.
|
||||
Tombstone StructCache[*gtsmodel.Tombstone]
|
||||
|
||||
// ThreadMute provides access to the gtsmodel ThreadMute database cache.
|
||||
ThreadMute StructCache[*gtsmodel.ThreadMute]
|
||||
|
||||
// User provides access to the gtsmodel User database cache.
|
||||
User StructCache[*gtsmodel.User]
|
||||
|
||||
|
|
8
internal/cache/size.go
vendored
8
internal/cache/size.go
vendored
|
@ -172,6 +172,8 @@ func totalOfRatios() float64 {
|
|||
return 0 +
|
||||
config.GetCacheAccountMemRatio() +
|
||||
config.GetCacheAccountNoteMemRatio() +
|
||||
config.GetCacheAccountSettingsMemRatio() +
|
||||
config.GetCacheAccountStatsMemRatio() +
|
||||
config.GetCacheApplicationMemRatio() +
|
||||
config.GetCacheBlockMemRatio() +
|
||||
config.GetCacheBlockIDsMemRatio() +
|
||||
|
@ -179,17 +181,21 @@ func totalOfRatios() float64 {
|
|||
config.GetCacheClientMemRatio() +
|
||||
config.GetCacheEmojiMemRatio() +
|
||||
config.GetCacheEmojiCategoryMemRatio() +
|
||||
config.GetCacheFilterMemRatio() +
|
||||
config.GetCacheFilterKeywordMemRatio() +
|
||||
config.GetCacheFilterStatusMemRatio() +
|
||||
config.GetCacheFollowMemRatio() +
|
||||
config.GetCacheFollowIDsMemRatio() +
|
||||
config.GetCacheFollowRequestMemRatio() +
|
||||
config.GetCacheFollowRequestIDsMemRatio() +
|
||||
config.GetCacheInReplyToIDsMemRatio() +
|
||||
config.GetCacheInstanceMemRatio() +
|
||||
config.GetCacheInReplyToIDsMemRatio() +
|
||||
config.GetCacheListMemRatio() +
|
||||
config.GetCacheListEntryMemRatio() +
|
||||
config.GetCacheMarkerMemRatio() +
|
||||
config.GetCacheMediaMemRatio() +
|
||||
config.GetCacheMentionMemRatio() +
|
||||
config.GetCacheMoveMemRatio() +
|
||||
config.GetCacheNotificationMemRatio() +
|
||||
config.GetCachePollMemRatio() +
|
||||
config.GetCachePollVoteMemRatio() +
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { SerializedError } from "@reduxjs/toolkit";
|
||||
import { FetchBaseQueryError } from "@reduxjs/toolkit/query";
|
||||
import React, { ReactNode } from "react";
|
||||
|
||||
function ErrorFallback({ error, resetErrorBoundary }) {
|
||||
return (
|
||||
|
@ -44,39 +46,59 @@ function ErrorFallback({ error, resetErrorBoundary }) {
|
|||
);
|
||||
}
|
||||
|
||||
function Error({ error }) {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.error("Rendering error:", error);
|
||||
let message;
|
||||
interface GtsError {
|
||||
error: string;
|
||||
error_description?: string;
|
||||
}
|
||||
|
||||
if (error.data != undefined) { // RTK Query error with data
|
||||
if (error.status) {
|
||||
message = (<>
|
||||
<b>{error.status}:</b> {error.data.error}
|
||||
{error.data.error_description &&
|
||||
<p>
|
||||
{error.data.error_description}
|
||||
</p>
|
||||
}
|
||||
</>);
|
||||
} else {
|
||||
message = error.data.error;
|
||||
}
|
||||
} else if (error.name != undefined || error.type != undefined) { // JS error
|
||||
message = (<>
|
||||
<b>{error.type && error.name}:</b> {error.message}
|
||||
</>);
|
||||
} else if (error.status && typeof error.error == "string") {
|
||||
message = (<>
|
||||
<b>{error.status}:</b> {error.error}
|
||||
</>);
|
||||
interface ErrorProps {
|
||||
error: FetchBaseQueryError | SerializedError | Error | undefined;
|
||||
reset?: () => void;
|
||||
}
|
||||
|
||||
function Error({ error, reset }: ErrorProps) {
|
||||
if (error === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.error("caught error: ", error);
|
||||
|
||||
let message: ReactNode;
|
||||
if ("status" in error) {
|
||||
// RTK Query error with data.
|
||||
const gtsError = error.data as GtsError;
|
||||
const errMsg = gtsError.error_description ?? gtsError.error;
|
||||
message = <>Code {error.status} {errMsg}</> ;
|
||||
} else {
|
||||
message = error.message ?? error;
|
||||
// SerializedError or Error.
|
||||
const errMsg = JSON.stringify(error);
|
||||
message = (
|
||||
<>
|
||||
<b>{error.name}:</b> {errMsg}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
let className = "error";
|
||||
if (reset) {
|
||||
className += " with-dismiss";
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="error">
|
||||
{message}
|
||||
<div className={className}>
|
||||
<span>{message}</span>
|
||||
{ reset &&
|
||||
<span
|
||||
className="dismiss"
|
||||
onClick={reset}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<span>Dismiss</span>
|
||||
<i className="fa fa-fw fa-close" aria-hidden="true" />
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -51,9 +51,9 @@ export default function MutationButton({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className={wrapperClassName}>
|
||||
<div className={wrapperClassName ? wrapperClassName : "mutation-button"}>
|
||||
{(showError && targetsThisButton && result.error) &&
|
||||
<Error error={result.error} />
|
||||
<Error error={result.error} reset={result.reset} />
|
||||
}
|
||||
<button
|
||||
type="submit"
|
||||
|
|
|
@ -257,33 +257,33 @@ input, select, textarea {
|
|||
overflow: auto;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.with-dismiss {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.dismiss {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mutation-button {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.messagebutton, .messagebutton > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
div.padded {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
button, .button {
|
||||
white-space: nowrap;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.messagebutton > div {
|
||||
button, .button {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.notImplemented {
|
||||
border: 2px solid rgb(70, 79, 88);
|
||||
background: repeating-linear-gradient(
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useMemo, useEffect } from "react";
|
||||
import React, { useMemo, useEffect, ReactNode } from "react";
|
||||
import { useFileInput, useComboBoxInput } from "../../../../lib/form";
|
||||
import useShortcode from "./use-shortcode";
|
||||
import useFormSubmit from "../../../../lib/form/submit";
|
||||
|
@ -29,50 +29,71 @@ import { useAddEmojiMutation } from "../../../../lib/query/admin/custom-emoji";
|
|||
import { useInstanceV1Query } from "../../../../lib/query/gts-api";
|
||||
|
||||
export default function NewEmojiForm() {
|
||||
const shortcode = useShortcode();
|
||||
|
||||
const { data: instance } = useInstanceV1Query();
|
||||
const emojiMaxSize = useMemo(() => {
|
||||
return instance?.configuration?.emojis?.emoji_size_limit ?? 50 * 1024;
|
||||
}, [instance]);
|
||||
|
||||
const image = useFileInput("image", {
|
||||
withPreview: true,
|
||||
maxSize: emojiMaxSize
|
||||
});
|
||||
const form = {
|
||||
shortcode: useShortcode(),
|
||||
image: useFileInput("image", {
|
||||
withPreview: true,
|
||||
maxSize: emojiMaxSize
|
||||
}),
|
||||
category: useComboBoxInput("category"),
|
||||
};
|
||||
|
||||
const category = useComboBoxInput("category");
|
||||
|
||||
const [submitForm, result] = useFormSubmit({
|
||||
shortcode, image, category
|
||||
}, useAddEmojiMutation());
|
||||
const [submitForm, result] = useFormSubmit(
|
||||
form,
|
||||
useAddEmojiMutation(),
|
||||
{
|
||||
changedOnly: false,
|
||||
// On submission, reset form values
|
||||
// no matter what the result was.
|
||||
onFinish: (_res) => {
|
||||
form.shortcode.reset();
|
||||
form.image.reset();
|
||||
form.category.reset();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (shortcode.value === undefined || shortcode.value.length == 0) {
|
||||
if (image.value != undefined) {
|
||||
let [name, _ext] = image.value.name.split(".");
|
||||
shortcode.setter(name);
|
||||
if (
|
||||
form.shortcode.value === undefined ||
|
||||
form.shortcode.value.length == 0
|
||||
) {
|
||||
if (form.image.value !== undefined) {
|
||||
// Suggest shortcode based on filename.
|
||||
let [name, _ext] = form.image.value.name.split(".");
|
||||
form.shortcode.setter(name);
|
||||
}
|
||||
}
|
||||
|
||||
/* We explicitly don't want to have 'shortcode' as a dependency here
|
||||
because we only want to change the shortcode to the filename if the field is empty
|
||||
at the moment the file is selected, not some time after when the field is emptied
|
||||
*/
|
||||
/* eslint-disable-next-line react-hooks/exhaustive-deps */
|
||||
}, [image.value]);
|
||||
// We explicitly don't want to have 'shortcode' as a
|
||||
// dependency here because we only want to change the
|
||||
// shortcode to the filename if the field is empty at
|
||||
// the moment the file is selected, not some time after
|
||||
// when the field is emptied.
|
||||
//
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [form.image.value]);
|
||||
|
||||
let emojiOrShortcode;
|
||||
|
||||
if (image.previewValue != undefined) {
|
||||
emojiOrShortcode = <img
|
||||
className="emoji"
|
||||
src={image.previewValue}
|
||||
title={`:${shortcode.value}:`}
|
||||
alt={shortcode.value}
|
||||
/>;
|
||||
} else if (shortcode.value !== undefined && shortcode.value.length > 0) {
|
||||
emojiOrShortcode = `:${shortcode.value}:`;
|
||||
let emojiOrShortcode: ReactNode;
|
||||
if (form.image.previewValue !== undefined) {
|
||||
emojiOrShortcode = (
|
||||
<img
|
||||
className="emoji"
|
||||
src={form.image.previewValue}
|
||||
title={`:${form.shortcode.value}:`}
|
||||
alt={form.shortcode.value}
|
||||
/>
|
||||
);
|
||||
} else if (
|
||||
form.shortcode.value !== undefined &&
|
||||
form.shortcode.value.length > 0
|
||||
) {
|
||||
emojiOrShortcode = `:${form.shortcode.value}:`;
|
||||
} else {
|
||||
emojiOrShortcode = `:your_emoji_here:`;
|
||||
}
|
||||
|
@ -87,22 +108,21 @@ export default function NewEmojiForm() {
|
|||
|
||||
<form onSubmit={submitForm} className="form-flex">
|
||||
<FileInput
|
||||
field={image}
|
||||
field={form.image}
|
||||
accept="image/png,image/gif,image/webp"
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
field={shortcode}
|
||||
field={form.shortcode}
|
||||
label="Shortcode, must be unique among the instance's local emoji"
|
||||
/>
|
||||
|
||||
<CategorySelect
|
||||
field={category}
|
||||
children={[]}
|
||||
field={form.category}
|
||||
/>
|
||||
|
||||
<MutationButton
|
||||
disabled={image.previewValue === undefined}
|
||||
disabled={form.image.previewValue === undefined || form.shortcode.value?.length === 0}
|
||||
label="Upload emoji"
|
||||
result={result}
|
||||
/>
|
||||
|
|
Loading…
Reference in a new issue