diff --git a/assets/brand/app-icon-1024.png b/assets/brand/app-icon-1024.png index cde5ab5d..6b12975e 100644 Binary files a/assets/brand/app-icon-1024.png and b/assets/brand/app-icon-1024.png differ diff --git a/assets/brand/app-icon-x1.png b/assets/brand/app-icon-x1.png index 5d9acb7f..e3414ff4 100644 Binary files a/assets/brand/app-icon-x1.png and b/assets/brand/app-icon-x1.png differ diff --git a/assets/brand/app-icon-x2.png b/assets/brand/app-icon-x2.png index 5551eca1..d6502379 100644 Binary files a/assets/brand/app-icon-x2.png and b/assets/brand/app-icon-x2.png differ diff --git a/assets/brand/app-icon-x3.png b/assets/brand/app-icon-x3.png index d445814a..75358436 100644 Binary files a/assets/brand/app-icon-x3.png and b/assets/brand/app-icon-x3.png differ diff --git a/assets/brand/app-icon.svg b/assets/brand/app-icon.svg index 821b870c..48ac3b8a 100644 --- a/assets/brand/app-icon.svg +++ b/assets/brand/app-icon.svg @@ -1,33 +1,40 @@ - + + - - - - + + + + + + + - - - + + + + + + + + - - - - - + + + + + + + + + - \ No newline at end of file + diff --git a/assets/brand/mark-mono-dark.svg b/assets/brand/mark-mono-dark.svg index 9afd7c2f..b82df255 100644 --- a/assets/brand/mark-mono-dark.svg +++ b/assets/brand/mark-mono-dark.svg @@ -1,14 +1,20 @@ - + + - + + + + + + + + + + + - - - - - \ No newline at end of file + + + diff --git a/assets/brand/mark.svg b/assets/brand/mark.svg index bee798f9..6746bb40 100644 --- a/assets/brand/mark.svg +++ b/assets/brand/mark.svg @@ -1,28 +1,33 @@ - + + - - - - + + + - - - + + + + + + + + - - - - - + + + + + + - \ No newline at end of file + diff --git a/docs/roadmap-resenha.md b/docs/roadmap-resenha.md index 0bab4d1f..df6b9ff9 100644 --- a/docs/roadmap-resenha.md +++ b/docs/roadmap-resenha.md @@ -29,6 +29,8 @@ código e os commits seguem em inglês, como o resto do repositório. | Busca: filtros embutidos e tradução | `50a8f12c` | Os filtros já existiam; faltava `de:fulano` e descoberta | | Emojis e figurinhas do servidor | `7d003021` | `:nome:` no markdown sem plugin; figurinha como mensagem inteira | | Fundação de i18n (en + pt-BR) | `688a1335` | `src/i18n/`; pt-BR é parcial e cai para inglês. Aba Idioma nas configurações | +| Rebrand: **Resenhacord** + logo nova | (ver git log) | `productName` mudou, `appId` **não** — assim o NSIS troca a instalação antiga no lugar. `userData` migra `Backspace` → `Resenhacord` no primeiro boot (senão todo mundo cairia da sessão). Logo: duas bolhas de conversa, roxo → rosa; fonte em `assets/brand/*.svg`, PNGs e ícones gerados por `pnpm gen-icons` | +| Aviso "X fixou uma mensagem" | (ver git log) | Tabela `messages` ganhou `type` (migração 0018); a rota de pin posta uma mensagem de sistema. Dois links: pular pra mensagem e abrir o painel de fixadas | | Menção `@everyone` | (ver git log) | Reusa o token `<@id>`; permissão `MENTION_EVERYONE` nova, concedida ao `@everyone` por padrão e retroativa via backfill no boot. Servidor neutraliza o token de quem não tem a permissão, no envio **e** na edição | | Renomear canal (nome e tópico) | (ver git log) | Rota `PATCH /api/channels/:id` já existia; faltava interface. Normalização em `shared/constants` para servidor e web usarem a mesma regra | | Volume da tela em 50% por padrão | (ver git log) | `DEFAULT_STREAM_VOLUME`; o publicador reforça o áudio do sistema, então o padrão anterior chegava alto demais | @@ -63,6 +65,11 @@ Ordem sugerida: as pequenas primeiro, as grandes uma de cada vez. | # | Feature | Tamanho | Observação técnica | |---|---|---|---| +## Próximo foco (pedido em 2026-09-02): personalização do usuário e do perfil + +A definir com o dono. Já existe: avatar, banner (imagem ou GIF), cor do avatar +e do banner, sobre mim, status personalizado, atividade (Spotify/jogos). + ## Pendente — ideias aprovadas | Feature | Tamanho | Observação técnica | diff --git a/packages/desktop/build/icon.icns b/packages/desktop/build/icon.icns index 3a7ea90d..47502ac9 100644 Binary files a/packages/desktop/build/icon.icns and b/packages/desktop/build/icon.icns differ diff --git a/packages/desktop/build/icon.ico b/packages/desktop/build/icon.ico index 26c5d417..afbcc481 100644 Binary files a/packages/desktop/build/icon.ico and b/packages/desktop/build/icon.ico differ diff --git a/packages/desktop/build/icon.png b/packages/desktop/build/icon.png index 5ad038bd..c366ed03 100644 Binary files a/packages/desktop/build/icon.png and b/packages/desktop/build/icon.png differ diff --git a/packages/desktop/build/icons/1024x1024.png b/packages/desktop/build/icons/1024x1024.png index a670e29d..9580695e 100644 Binary files a/packages/desktop/build/icons/1024x1024.png and b/packages/desktop/build/icons/1024x1024.png differ diff --git a/packages/desktop/build/icons/128x128.png b/packages/desktop/build/icons/128x128.png index 73fe02fa..dc00d0c5 100644 Binary files a/packages/desktop/build/icons/128x128.png and b/packages/desktop/build/icons/128x128.png differ diff --git a/packages/desktop/build/icons/16x16.png b/packages/desktop/build/icons/16x16.png index 8a4c0db3..06a76900 100644 Binary files a/packages/desktop/build/icons/16x16.png and b/packages/desktop/build/icons/16x16.png differ diff --git a/packages/desktop/build/icons/256x256.png b/packages/desktop/build/icons/256x256.png index 71989079..63508950 100644 Binary files a/packages/desktop/build/icons/256x256.png and b/packages/desktop/build/icons/256x256.png differ diff --git a/packages/desktop/build/icons/32x32.png b/packages/desktop/build/icons/32x32.png index 70250d7c..167cb4f0 100644 Binary files a/packages/desktop/build/icons/32x32.png and b/packages/desktop/build/icons/32x32.png differ diff --git a/packages/desktop/build/icons/48x48.png b/packages/desktop/build/icons/48x48.png index fc6737b3..b015098d 100644 Binary files a/packages/desktop/build/icons/48x48.png and b/packages/desktop/build/icons/48x48.png differ diff --git a/packages/desktop/build/icons/512x512.png b/packages/desktop/build/icons/512x512.png index 5ad038bd..c366ed03 100644 Binary files a/packages/desktop/build/icons/512x512.png and b/packages/desktop/build/icons/512x512.png differ diff --git a/packages/desktop/build/icons/64x64.png b/packages/desktop/build/icons/64x64.png index 6ab93cfa..b86a2b13 100644 Binary files a/packages/desktop/build/icons/64x64.png and b/packages/desktop/build/icons/64x64.png differ diff --git a/packages/desktop/electron-builder.yml b/packages/desktop/electron-builder.yml index 0c5cc7dd..ea28f6bc 100644 --- a/packages/desktop/electron-builder.yml +++ b/packages/desktop/electron-builder.yml @@ -1,5 +1,5 @@ appId: com.backspace.desktop -productName: Backspace +productName: Resenhacord artifactName: "${productName}-${version}-${arch}.${ext}" directories: output: dist-electron @@ -36,7 +36,7 @@ publish: - provider: generic url: https://git.resenha.website/devsyncwrld/backspace/releases/download/latest/ protocols: - - name: Backspace + - name: Resenhacord schemes: - backspace mac: diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 44173852..fab22a82 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -3,7 +3,7 @@ "version": "1.5.0", "private": true, "license": "AGPL-3.0-only", - "description": "Backspace", + "description": "Resenhacord", "author": { "name": "Jannis Braun", "email": "backspace@backspace.chat" diff --git a/packages/desktop/resources/instance-picker.html b/packages/desktop/resources/instance-picker.html index b427dae0..06a633c3 100644 --- a/packages/desktop/resources/instance-picker.html +++ b/packages/desktop/resources/instance-picker.html @@ -3,7 +3,7 @@ - Backspace — Connect + Resenhacord — Conectar + + + + + + + + + + + + + - - - - - + + + + + + - \ No newline at end of file + diff --git a/packages/web/public/icons/logo.png b/packages/web/public/icons/logo.png index 71989079..63508950 100644 Binary files a/packages/web/public/icons/logo.png and b/packages/web/public/icons/logo.png differ diff --git a/packages/web/src/components/JoinPage.tsx b/packages/web/src/components/JoinPage.tsx index 5faa0cdf..a1a633bb 100644 --- a/packages/web/src/components/JoinPage.tsx +++ b/packages/web/src/components/JoinPage.tsx @@ -232,7 +232,7 @@ export function JoinPage() { onClick={() => navigate('/channels/@me')} className="px-6 py-2.5 bg-accent-primary hover:bg-accent-primary/80 text-white font-medium rounded transition-colors" > - Back to Backspace + {t('join.page.back')} ) : ( prevState.realtimeMessageEvents.length) { const newEvents = state.realtimeMessageEvents.slice(prevState.realtimeMessageEvents.length); for (const { message } of newEvents) { - if (message.userId !== currentUser?.id) { + // System notices (pins, membership) are not from anyone — no toast. + if (message.type !== 'system' && message.userId !== currentUser?.id) { if (focused && message.channelId === currentChannel) break; const displayName = message.user?.displayName || message.user?.username || 'Someone'; diff --git a/packages/web/src/components/chat/SystemMessage.test.tsx b/packages/web/src/components/chat/SystemMessage.test.tsx index 63ebdfe7..f7ea98e4 100644 --- a/packages/web/src/components/chat/SystemMessage.test.tsx +++ b/packages/web/src/components/chat/SystemMessage.test.tsx @@ -96,3 +96,26 @@ describe('SystemMessage — icon_changed', () => { expect(screen.getByText(/Heidi updated the group icon/)).toBeDefined(); }); }); + +describe('SystemMessage — message_pinned (space channel, no roster)', () => { + it('names the actor from message.user and offers both links', async () => { + const { useUIStore } = await import('../../stores/uiStore'); + const msg = buildMessage({ event: 'message_pinned', messageId: 'M9' }); + renderSM(msg, null); + + expect(screen.getByText(/Heidi pinned/)).toBeDefined(); + + const before = useUIStore.getState().pinsPanelRequest; + screen.getByText('a message').click(); + expect(useUIStore.getState().jumpToMessageRequest).toBe('M9'); + + screen.getByText('See all pinned messages.').click(); + expect(useUIStore.getState().pinsPanelRequest).toBe(before + 1); + }); + + it('falls back to the plain renderer when messageId is missing', () => { + // A malformed payload must not throw — it degrades to the generic branch. + const msg = buildMessage({ event: 'message_pinned' }); + expect(() => renderSM(msg, null)).not.toThrow(); + }); +}); diff --git a/packages/web/src/components/chat/SystemMessage.tsx b/packages/web/src/components/chat/SystemMessage.tsx index 4d088949..36ed8997 100644 --- a/packages/web/src/components/chat/SystemMessage.tsx +++ b/packages/web/src/components/chat/SystemMessage.tsx @@ -1,6 +1,7 @@ import type { DmChannel, MessageWithUser, SpaceInviteSystemPayload, User } from '@backspace/shared'; import { SpaceInviteCard } from './SpaceInviteCard'; import { useT } from '../../i18n'; +import { useUIStore } from '../../stores/uiStore'; interface SystemMessageProps { message: MessageWithUser; @@ -52,6 +53,33 @@ export function SystemMessage({ message, dm }: SystemMessageProps) { ); } + // The pin notice has two links in one sentence — the pinned message, and the + // pins panel — so it is assembled from parts around the placeholders rather + // than as flat text. Word order stays with the translation. + if (data.event === 'message_pinned' && typeof data.messageId === 'string') { + const pinnedId = data.messageId; + const parts = t('system.pinned', { actor: actorName, message: '\u0000', seeAll: '\u0001' }); + const [beforeMsg, rest = ''] = parts.split('\u0000'); + const [between, after = ''] = rest.split('\u0001'); + const linkClass = 'text-txt-secondary hover:text-txt-primary hover:underline cursor-pointer'; + return ( +
+ + 📌 + {beforeMsg} + useUIStore.getState().requestJumpToMessage(pinnedId)}> + {t('system.pinned.message')} + + {between} + useUIStore.getState().requestPinsPanel()}> + {t('system.pinned.seeAll')} + + {after} + +
+ ); + } + // Inline-text events. let text = ''; let icon = ''; diff --git a/packages/web/src/components/layout/AppLayout.tsx b/packages/web/src/components/layout/AppLayout.tsx index 01314d7b..c689925a 100644 --- a/packages/web/src/components/layout/AppLayout.tsx +++ b/packages/web/src/components/layout/AppLayout.tsx @@ -353,7 +353,7 @@ export function AppLayout() { if (!user || showBootSkeleton) { return ( -
+
{/* Space strip */}
{Array.from({ length: 5 }, (_, i) => ( diff --git a/packages/web/src/components/layout/MainContent.tsx b/packages/web/src/components/layout/MainContent.tsx index 4d36de68..ea84fe42 100644 --- a/packages/web/src/components/layout/MainContent.tsx +++ b/packages/web/src/components/layout/MainContent.tsx @@ -56,6 +56,20 @@ export function MainContent() { const [searchOpen, setSearchOpen] = useState(false); const [pinsOpen, setPinsOpen] = useState(false); const [jumpToMessageId, setJumpToMessageId] = useState(null); + const pinsPanelRequest = useUIStore((st) => st.pinsPanelRequest); + const jumpToMessageRequest = useUIStore((st) => st.jumpToMessageRequest); + const clearJumpToMessageRequest = useUIStore((st) => st.clearJumpToMessageRequest); + // Requests arrive from the pin system notice (see SystemMessage). The + // counter starts at 0 and only ever increments, so the initial render is a + // no-op and every later change is a real click. + useEffect(() => { + if (pinsPanelRequest > 0) setPinsOpen(true); + }, [pinsPanelRequest]); + useEffect(() => { + if (!jumpToMessageRequest) return; + setJumpToMessageId(jumpToMessageRequest); + clearJumpToMessageRequest(); + }, [jumpToMessageRequest, clearJumpToMessageRequest]); // Resolve the DM header's "first other" member through the canonical view // cache. The hook must be called unconditionally at the component top, so we diff --git a/packages/web/src/components/layout/SpaceSidebar.tsx b/packages/web/src/components/layout/SpaceSidebar.tsx index 659a3c26..1d78ff10 100644 --- a/packages/web/src/components/layout/SpaceSidebar.tsx +++ b/packages/web/src/components/layout/SpaceSidebar.tsx @@ -111,7 +111,7 @@ function SidebarItem({ id, name, icon, avatarColor, active, onClick, onContextMe const buttonContent = (