feat(files): загрузка и выдача вложений
- POST /api/v1/channels/{id}/files (multipart, ATTACH_FILES): файл пишется на
диск под идентификатором, в БД хранятся метаданные и sha256; имя файла
очищается от путей и управляющих символов, объём ограничен MAX_UPLOAD_SIZE;
- GET/HEAD /files/{id}: содержимое с проверкой прав на комнату сообщения
(невидимая комната и чужие вложения → 404), ETag, immutable-кэш,
Content-Disposition с RFC 5987 для не-ASCII имён;
- вложения привязываются к сообщению при отправке (attachment_ids), сироты
ищутся через store.ListOrphanFiles для обслуживания;
- GET /api/v1/files/{id} — метаданные файла для клиента;
- chi-ручки теперь умеют отдавать huma-ошибки в общем конверте
(writeHumaAPIError), иначе 404 превращался в 500;
- READY отдаёт реальные read states пользователя.
Тесты: загрузка, скачивание участником, скрытая комната → 404, вложение в
сообщении, проверка подписи ETag.
This commit is contained in:
@@ -26,8 +26,7 @@ export function GuildGeneralSection({ guild, canEdit }: GuildGeneralSectionProps
|
||||
const [saved, setSaved] = useState(false);
|
||||
|
||||
const save = useMutation({
|
||||
mutationFn: () =>
|
||||
updateGuild(guild.id, { name: name.trim(), description: description.trim() }),
|
||||
mutationFn: () => updateGuild(guild.id, { name: name.trim(), description: description.trim() }),
|
||||
onSuccess: (updated) => {
|
||||
queryClient.setQueryData(guildQueryKey(guild.id), updated);
|
||||
// Рейка и сайдбар берут имя из снапшота — обновляем и его.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState, type FormEvent } from 'react';
|
||||
import { useState, type FormEvent } from 'react';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -29,23 +29,18 @@ export function GuildNicknameSection({
|
||||
}: GuildNicknameSectionProps) {
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
const [nickname, setNickname] = useState(currentNickname);
|
||||
const [edited, setEdited] = useState(false);
|
||||
// `null` — поле ещё не трогали, поэтому показываем ник из снапшота/участников
|
||||
// (он может прийти уже после монтирования секции).
|
||||
const [typed, setTyped] = useState<string | null>(null);
|
||||
const [saved, setSaved] = useState(false);
|
||||
|
||||
// Ник может прийти из списка участников уже после монтирования секции:
|
||||
// подтягиваем его, пока пользователь не начал править поле сам.
|
||||
useEffect(() => {
|
||||
if (!edited) {
|
||||
setNickname(currentNickname);
|
||||
}
|
||||
}, [currentNickname, edited]);
|
||||
const nickname = typed ?? currentNickname;
|
||||
|
||||
const save = useMutation({
|
||||
mutationFn: (value: string) => updateGuildMember(guildId, userId ?? '', { nickname: value }),
|
||||
onSuccess: (_result, value) => {
|
||||
useSessionStore.getState().setMyNickname(guildId, value);
|
||||
void queryClient.invalidateQueries({ queryKey: guildMembersQueryKey(guildId) });
|
||||
setTyped(null);
|
||||
setSaved(true);
|
||||
},
|
||||
});
|
||||
@@ -81,10 +76,7 @@ export function GuildNicknameSection({
|
||||
value={nickname}
|
||||
maxLength={32}
|
||||
disabled={userId === undefined}
|
||||
onChange={(event) => {
|
||||
setEdited(true);
|
||||
setNickname(event.target.value);
|
||||
}}
|
||||
onChange={(event) => setTyped(event.target.value)}
|
||||
/>
|
||||
|
||||
{save.isError ? <ErrorNotice error={save.error} /> : null}
|
||||
|
||||
@@ -54,7 +54,10 @@ export default function ServersSettingsPage() {
|
||||
}
|
||||
}, [sessionGuilds.length, myGuilds.data, upsertGuild]);
|
||||
|
||||
const guilds = sessionGuilds.length > 0 ? sessionGuilds : (myGuilds.data ?? []);
|
||||
const guilds = useMemo(
|
||||
() => (sessionGuilds.length > 0 ? sessionGuilds : (myGuilds.data ?? [])),
|
||||
[sessionGuilds, myGuilds.data],
|
||||
);
|
||||
const active = useMemo(
|
||||
() => (routeGuildId === undefined ? null : (guilds.find((g) => g.id === routeGuildId) ?? null)),
|
||||
[guilds, routeGuildId],
|
||||
|
||||
Reference in New Issue
Block a user