fix(web): баннер сервера из снапшота, приглашение, встроенные звуки

- READY-снапшот больше не теряет banner/splash/accent: шапка сервера
  показывает баннер сразу после подключения (AGENT.md 7.4)
- GUILD_UPDATE несёт всё оформление, клиент применяет его без перезагрузки
- кнопка приглашения в шапке сервера со ссылкой и копированием
- встроенные звуки интерфейса на Web Audio с разблокировкой по жесту
- object URL превью через безопасные обёртки (jsdom и урезанные webview)
This commit is contained in:
2026-09-21 00:04:49 +03:00
parent 6882516340
commit 4ea2965892
22 changed files with 989 additions and 56 deletions
+1 -6
View File
@@ -173,12 +173,7 @@ func (s *Server) finishAppearanceChange(
}
s.invalidateGuild(guildID)
s.recordAudit(ctx, user, guildID, "guild."+kind.label+"."+action, "guild", &guildID, "")
if s.gateway != nil {
s.gateway.Dispatch("GUILD_UPDATE", map[string]any{
"guild_id": formatSnowflake(guildID),
"name": updated.Name,
})
}
s.dispatchGuildUpdate(*updated)
output, err := s.guildOutput(ctx, user, *updated)
if err != nil {
writeHumaAPIError(w, err)
+32 -6
View File
@@ -269,12 +269,7 @@ func (s *Server) registerGuildCoreRoutes(api huma.API) {
return nil, humaError(err)
}
s.invalidateGuild(guildID)
if s.gateway != nil {
s.gateway.Dispatch("GUILD_UPDATE", map[string]any{
"guild_id": formatSnowflake(guildID),
"name": guild.Name,
})
}
s.dispatchGuildUpdate(*guild)
return s.guildOutput(ctx, user, *guild)
})
@@ -1519,6 +1514,37 @@ func (s *Server) joinGuild(ctx context.Context, user *store.User, guildID uint64
return nil
}
// dispatchGuildUpdate рассылает GUILD_UPDATE со всеми полями оформления:
// клиенты обновляют иконку, баннер, splash и акцент без перезагрузки
// (AGENT.md 7.4, 11.6).
func (s *Server) dispatchGuildUpdate(guild store.Guild) {
if s.gateway == nil {
return
}
payload := map[string]any{
"guild_id": formatSnowflake(guild.ID),
"name": guild.Name,
"description": guild.Description,
"accent_color": guild.AccentColor,
}
if guild.IconFileID != nil {
payload["icon_file_id"] = formatSnowflake(*guild.IconFileID)
} else {
payload["icon_file_id"] = nil
}
if guild.BannerFileID != nil {
payload["banner_file_id"] = formatSnowflake(*guild.BannerFileID)
} else {
payload["banner_file_id"] = nil
}
if guild.SplashFileID != nil {
payload["splash_file_id"] = formatSnowflake(*guild.SplashFileID)
} else {
payload["splash_file_id"] = nil
}
s.gateway.Dispatch("GUILD_UPDATE", payload)
}
// deleteGuild удаляет сервер: главный сервер удалить нельзя (AGENT.md 6.3).
func (s *Server) deleteGuild(ctx context.Context, actor *store.User, guildID uint64) error {
guild, err := s.store.GetGuild(ctx, guildID)