From 1d8685a4ef7898a02b41d1778d1e9b8cdd9cf7ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Bellegarde?= Date: Thu, 6 Nov 2025 11:21:26 +0100 Subject: [PATCH 01/20] stylesheets: Tweak CSS for mobile --- stylesheets/components/Droidian.scss | 131 +++++++++++++++++++++++++ stylesheets/components/NavSidebar.scss | 31 +----- stylesheets/manifest.scss | 2 + 3 files changed, 136 insertions(+), 28 deletions(-) create mode 100644 stylesheets/components/Droidian.scss diff --git a/stylesheets/components/Droidian.scss b/stylesheets/components/Droidian.scss new file mode 100644 index 000000000..b86e99aea --- /dev/null +++ b/stylesheets/components/Droidian.scss @@ -0,0 +1,131 @@ +.Preferences > .NavSidebar { + &:has(.NavSidebar__Header) { + max-width: 70px !important; + } +} + +.module-ConversationHeader__header { + justify-content: center; +} + +.module-ConversationHeader__button.module-ConversationHeader__button--video { + display: none !important; +} +.module-conversation-list__item--contact-or-conversation__avatar-container { + margin-left: 4px !important; + .module-conversation-list__item--contact-or-conversation__unread-indicator { + margin-right: 4px !important; + } +} +::-webkit-scrollbar { + display: none !important; +} +.module-reaction-viewer { + width: 220px !important; + height: 220px !important; +} +.Preferences__page-selector { + min-width: 48px !important; + padding-top: 0px !important; +} +.Preferences__button { + font-size: 0px !important; + width: 48px !important; +} +.Preferences__profile-chip { + margin-inline-start: 8px !important; + padding-inline-start: 8px !important; +} +.Preferences { + width: 100% !important; +} +.Preferences__control { + flex-direction: column !important; +} +.ConversationDetails-panel-row__root { + flex-direction: column !important; + align-items: stretch !important; +} + +.module-message--incoming { + .module-message__buttons { + opacity: 1 !important; + } +} +.module-sticker-manager__preview-modal__modal.module-Modal { + width: initial !important; +} +.react-contextmenu-submenu { + >.react-contextmenu { + right: 10px !important; + } +} +.module-InstallScreenQrCodeNotScannedStep__contents { + flex-direction: column !important; + top: 44px !important; +} +.module-InstallScreenQrCodeNotScannedStep__qr-code { + margin-inline: 50% !important; +} + +.Lightbox__main-container { + min-height: 100% !important; +} +.Lightbox__zoomable-container { + margin-inline: 0px !important; +} + +.CompositionArea__button-cell:first-child { + margin-inline-start: 5px !important; +} + +.CompositionArea__button-cell { + margin-inline: 1px !important; +} + +.CompositionArea__toggle-large { + display: none; +} + +.NavTabs__Container { + flex-direction: column-reverse !important; +} + +.NavTabs { + height: initial !important; + width: 100% !important; + flex-direction: row !important; +} + +.NavTabs__Item.NavTabs__Toggle { + display: none !important; +} + +.NavTabs__TabList { + display: flex !important; + flex-direction: row !important; +} + +.NavTabs__TabPanel { + overflow: hidden !important; +} + +.CallsTab__EmptyState { + display: none !important; +} + +.CallsTab__ConversationCallDetails { + display: none !important; +} + +.Stories__placeholder { + display: none !important; +} + +.NavSidebar { + width: 100% !important; +} + +.module-tooltip { + display: none !important; +} diff --git a/stylesheets/components/NavSidebar.scss b/stylesheets/components/NavSidebar.scss index ba5f39d48..21540684c 100644 --- a/stylesheets/components/NavSidebar.scss +++ b/stylesheets/components/NavSidebar.scss @@ -143,37 +143,12 @@ flex-direction: column; } -.NavSidebar__DragHandle { - position: absolute; - z-index: variables.$z-index-above-above-base; - top: 0; - bottom: 0; - inset-inline-start: 100%; - width: 8px; - background: transparent; +.NavSidebar__document--draggingHandle { cursor: col-resize; - // Disable browser handling of gestures so element can be dragged with touch events - touch-action: none; - - &:focus { - outline: none; - @include mixins.keyboard-mode { - box-shadow: inset 0 0 0 2px variables.$color-ultramarine; - } - } } -.NavSidebar__DragHandle--dragging { - @include mixins.light-theme { - background-color: variables.$color-black-alpha-12; - } - @include mixins.dark-theme { - background-color: variables.$color-white-alpha-12; - } -} - -.NavSidebar__document--draggingHandle { - cursor: col-resize; +#LeftPane { + width: 100%; } .NavSidebar__HeaderActions { diff --git a/stylesheets/manifest.scss b/stylesheets/manifest.scss index 953f4987e..76983e70b 100644 --- a/stylesheets/manifest.scss +++ b/stylesheets/manifest.scss @@ -203,3 +203,5 @@ @use 'components/UsernameMegaphone.scss'; @use 'components/UsernameOnboardingModal.scss'; @use 'components/WhatsNew.scss'; + +@use 'components/Droidian.scss'; From 270ba60abed7e1456e04903a489d9ec320ec491c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Bellegarde?= Date: Thu, 6 Nov 2025 11:29:55 +0100 Subject: [PATCH 02/20] ts: Tweak conversation view for mobile phones --- ts/components/ChatsTab.dom.tsx | 58 ++---- ts/components/NavSidebar.dom.tsx | 192 +++++------------- ts/components/Preferences.dom.tsx | 2 +- .../conversation/ConversationHeader.dom.tsx | 16 ++ ts/state/ducks/conversations.preload.ts | 9 +- ts/state/selectors/items.dom.ts | 2 +- 6 files changed, 95 insertions(+), 184 deletions(-) diff --git a/ts/components/ChatsTab.dom.tsx b/ts/components/ChatsTab.dom.tsx index 636acc482..62ec8d615 100644 --- a/ts/components/ChatsTab.dom.tsx +++ b/ts/components/ChatsTab.dom.tsx @@ -42,45 +42,31 @@ export function ChatsTab({ }: ChatsTabProps): React.JSX.Element { return ( <> -
- {renderLeftPane({ - otherTabsUnreadStats, - collapsed: navTabsCollapsed, - hasPendingUpdate, - hasFailedStorySends, - onToggleCollapse: onToggleNavTabsCollapse, - })} -
+ + {!selectedConversationId && +
+ {renderLeftPane({ + otherTabsUnreadStats, + hasPendingUpdate, + hasFailedStorySends, + onToggleCollapse: onToggleNavTabsCollapse, + })} +
+ } + + {selectedConversationId &&
- {selectedConversationId ? ( -
- {renderConversationView({ selectedConversationId })} -
- ) : ( -
- {renderMiniPlayer({ shouldFlow: false })} -
-

- {isStaging - ? 'THIS IS A STAGING DESKTOP' - : i18n('icu:welcomeToSignal')} -

-

- -

-
-
- {i18n('icu:signalNonProfit')} -
-
- )} +
+ {renderConversationView({ selectedConversationId })} +
+ } ); } diff --git a/ts/components/NavSidebar.dom.tsx b/ts/components/NavSidebar.dom.tsx index 42e3b9829..14cc780d5 100644 --- a/ts/components/NavSidebar.dom.tsx +++ b/ts/components/NavSidebar.dom.tsx @@ -88,159 +88,61 @@ export function NavSidebar({ renderToastManager, }: NavSidebarProps): React.JSX.Element { const isRTL = i18n.getLocaleDirection() === 'rtl'; - const [dragState, setDragState] = useState(DragState.INITIAL); - - const [preferredWidth, setPreferredWidth] = useState(() => { - return getWidthFromPreferredWidth(preferredLeftPaneWidth, { - requiresFullWidth, - }); - }); - - const width = getWidthFromPreferredWidth(preferredWidth, { - requiresFullWidth, - }); - - const widthBreakpoint = getNavSidebarWidthBreakpoint(width); - - const expandNarrowLeftPane = useCallback(() => { - if (preferredWidth < MIN_FULL_WIDTH) { - setPreferredWidth(MIN_FULL_WIDTH); - savePreferredLeftPaneWidth(MIN_FULL_WIDTH); - } - }, [preferredWidth, savePreferredLeftPaneWidth]); - - // `useMove` gives us keyboard and mouse dragging support. - const { moveProps } = useMove({ - onMoveStart() { - setDragState(DragState.DRAGGING); - }, - onMoveEnd() { - setDragState(DragState.DRAGEND); - }, - onMove(event) { - const { shiftKey, pointerType } = event; - const deltaX = isRTL ? -event.deltaX : event.deltaX; - const isKeyboard = pointerType === 'keyboard'; - const increment = isKeyboard && shiftKey ? 10 : 1; - setPreferredWidth(prevWidth => { - // Jump minimize for keyboard users - if (isKeyboard && prevWidth === MIN_FULL_WIDTH && deltaX < 0) { - return MIN_WIDTH; - } - // Jump maximize for keyboard users - if (isKeyboard && prevWidth === MIN_WIDTH && deltaX > 0) { - return MIN_FULL_WIDTH; - } - return prevWidth + deltaX * increment; - }); - }, - }); - - useEffect(() => { - // Save the preferred width when the drag ends. We can't do this in onMoveEnd - // because the width is not updated yet. - if (dragState === DragState.DRAGEND) { - setPreferredWidth(width); - savePreferredLeftPaneWidth(width); - setDragState(DragState.INITIAL); - } - }, [ - dragState, - preferredLeftPaneWidth, - preferredWidth, - savePreferredLeftPaneWidth, - width, - ]); - - useEffect(() => { - // This effect helps keep the pointer `col-resize` even when you drag past the handle. - const className = 'NavSidebar__document--draggingHandle'; - if (dragState === DragState.DRAGGING) { - document.body.classList.add(className); - return () => { - document.body.classList.remove(className); - }; - } - return undefined; - }, [dragState]); return ( - -
- {!hideHeader && ( -
- {onBack == null && navTabsCollapsed && ( - +
+ {!hideHeader && ( +
+ {onBack == null && navTabsCollapsed && ( + + )} +
+ {onBack != null && ( + )} -
- {onBack != null && ( - - )} -

- {title} -

- {actions && ( -
{actions}
- )} -
+ {title} + + {actions && ( +
{actions}
+ )}
- )} +
+ )} -
{children}
+
{children}
-
- - {renderToastManager({ - containerWidthBreakpoint: widthBreakpoint, - expandNarrowLeftPane, - })} -
- + {renderToastManager({ containerWidthBreakpoint: 400 })} +
); } diff --git a/ts/components/Preferences.dom.tsx b/ts/components/Preferences.dom.tsx index 8f6857426..c5afb4ad9 100644 --- a/ts/components/Preferences.dom.tsx +++ b/ts/components/Preferences.dom.tsx @@ -2338,7 +2338,7 @@ export function Preferences({
void; onViewConversationDetails: () => void; }) { + const dispatch = useDispatch(); + const onBackButton = () => { + dispatch(showConversation({ conversationId: undefined })); + }; + let onClick: undefined | (() => void); const { type } = conversation; switch (type) { @@ -556,6 +564,13 @@ function HeaderContent({ if (onClick) { return ( + <> +
+ ); } diff --git a/ts/state/ducks/conversations.preload.ts b/ts/state/ducks/conversations.preload.ts index eaeb31d52..a577a7214 100644 --- a/ts/state/ducks/conversations.preload.ts +++ b/ts/state/ducks/conversations.preload.ts @@ -4796,7 +4796,7 @@ type ShowConversationArgsType = ReadonlyDeep<{ export type ShowConversationType = ReadonlyDeep< (options: ShowConversationArgsType) => unknown >; - +export { showConversation }; function showConversation({ conversationId, messageId, @@ -4869,6 +4869,13 @@ function showConversation({ return; } + const navtabs = document.getElementsByClassName('NavTabs') as HTMLElement; + if (conversationId == undefined) { + navtabs[0].style.display = 'block'; + } else { + navtabs[0].style.display = 'none'; + } + // notify composer in case we need to stop recording a voice note if ( originalLocation.tab === NavTab.Chats && diff --git a/ts/state/selectors/items.dom.ts b/ts/state/selectors/items.dom.ts index 6e71b4038..d83a0a0dc 100644 --- a/ts/state/selectors/items.dom.ts +++ b/ts/state/selectors/items.dom.ts @@ -258,7 +258,7 @@ export const getTextFormattingEnabled = createSelector( export const getNavTabsCollapsed = createSelector( getItems, - (state: ItemsStateType): boolean => Boolean(state.navTabsCollapsed ?? false) + (state: ItemsStateType): boolean => Boolean(false) ); export const getShowStickersIntroduction = createSelector( From 19398a51bf81f01778876dcdfe3d6ec95ed2a3d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Bellegarde?= Date: Wed, 5 Nov 2025 18:18:32 +0100 Subject: [PATCH 03/20] app: main: Simplify second instance startup Really slow on a phone, this commit does not really fix the issue :( --- app/main.main.ts | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/app/main.main.ts b/app/main.main.ts index a906a6beb..4ffa78639 100644 --- a/app/main.main.ts +++ b/app/main.main.ts @@ -263,20 +263,7 @@ if (!process.mas) { app.exit(); } else { app.on('second-instance', (_e: Electron.Event, argv: Array) => { - // Workaround to let AllowSetForegroundWindow succeed. - // See https://www.npmjs.com/package/@signalapp/windows-dummy-keystroke for a full explanation of why this is needed. - if (OS.isWindows()) { - sendDummyKeystroke(); - } - - // Someone tried to run a second instance, we should focus our window - if (mainWindow) { - if (mainWindow.isMinimized()) { - mainWindow.restore(); - } - - showWindow(); - } + showWindow(); const route = maybeGetIncomingSignalRoute(argv); if (route != null) { From 5bac4c0204c61dca786908ff3b120b0548f78b60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Bellegarde?= Date: Wed, 30 Jul 2025 23:46:21 +0200 Subject: [PATCH 04/20] ts: components: SimpleQuillWrapper: Prevent auto-focus on mount for mobile When entering a conversation, signal-quill-cjs automatically focuses the editor, triggering the on-screen keyboard on mobile devices. This behavior is undesirable, as it suggests immediate typing is required. To prevent this: - The editor is now initially rendered with the inert attribute, making it non-focusable and non-interactive. - The inert attribute is removed on user click, allowing interaction and manual focus. This improves the user experience on mobile by preventing the keyboard from appearing until explicitly needed. --- ts/components/SimpleQuillWrapper.dom.tsx | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/ts/components/SimpleQuillWrapper.dom.tsx b/ts/components/SimpleQuillWrapper.dom.tsx index 7ac9ad413..9d928de72 100644 --- a/ts/components/SimpleQuillWrapper.dom.tsx +++ b/ts/components/SimpleQuillWrapper.dom.tsx @@ -1,7 +1,7 @@ // Copyright 2025 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import React, { createRef } from 'react'; +import React, { createRef, useCallback } from 'react'; import Quill from '@signalapp/quill-cjs'; import Emitter from '@signalapp/quill-cjs/core/emitter.js'; import type { Delta } from '@signalapp/quill-cjs'; @@ -62,7 +62,7 @@ export class SimpleQuillWrapper extends React.Component { formats: this.props.formats, modules: this.props.modules, placeholder: this.props.placeholder, - readOnly: this.props.readOnly, + readOnly: true }); } @@ -71,10 +71,24 @@ export class SimpleQuillWrapper extends React.Component { return this.quill; } + handleClick = () => { + const el = this.quillElement.current; + if (el) { + el.removeAttribute('inert'); + } + // Focus the Quill editor programmatically + if (this.quill) { + this.quill.focus(); + } + }; + override render(): React.JSX.Element { return ( -
-
+
+
); } From e3a5b444bbb27ac71b7f8712a32fd88bdeb85c6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Bellegarde?= Date: Fri, 1 Aug 2025 15:34:08 +0200 Subject: [PATCH 05/20] ts: ActiveWindowService: consider screensaver state in isActive() Subscribe to the `ActiveChanged` signal from `org.gnome.ScreenSaver` via DBus and maintain internal state to reflect screensaver activity. The `isActive()` method now returns `false` when the screensaver is active, ensuring that application logic properly respects user inactivity. Useful for improving notifications behavior when the user is away. Only GNOME is supported at this stage; other desktop environments could be added later. --- package.json | 1 + ts/services/ActiveWindowService.std.ts | 34 +++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 633ec1e10..a6ab6cd97 100644 --- a/package.json +++ b/package.json @@ -156,6 +156,7 @@ "country-codes-list": "2.0.0", "credit-card-type": "10.0.2", "dashdash": "2.0.0", + "dbus-native": "0.4.0", "direction": "1.0.4", "dom-accessibility-api": "0.7.0", "emoji-datasource": "16.0.0", diff --git a/ts/services/ActiveWindowService.std.ts b/ts/services/ActiveWindowService.std.ts index 5cf84aa37..3e4a62afc 100644 --- a/ts/services/ActiveWindowService.std.ts +++ b/ts/services/ActiveWindowService.std.ts @@ -3,6 +3,7 @@ import { SECOND } from '../util/durations/constants.std.js'; import { throttle } from '../util/throttle.std.js'; +import dbus from 'dbus-native'; // Idle timer - you're active for ACTIVE_TIMEOUT after one of these events const ACTIVE_TIMEOUT = 15 * SECOND; @@ -31,6 +32,8 @@ class ActiveWindowService { #isInitialized = false; #isFocused = false; + #screenSaverIface = null; + #screensaverActive: boolean = false; #activeCallbacks: Array<() => void> = []; #changeCallbacks: Array<(isActive: boolean) => void> = []; #lastActiveEventAt = -Infinity; @@ -53,6 +56,31 @@ class ActiveWindowService { } this.#isInitialized = true; + const sessionBus = dbus.sessionBus(); + const service = sessionBus.getService('org.gnome.ScreenSaver'); + + service.getInterface( + '/org/gnome/ScreenSaver', + 'org.gnome.ScreenSaver', + (err: Error | null, iface: any) => { + if (err) { + log.warn('Failed to connect to org.gnome.ScreenSaver:', err); + return; + } + this.#screenSaverIface = iface; + + iface.GetActive((err: Error | null, active: boolean) => { + if (!err) { + this.#screensaverActive = active; + } + }); + + iface.on('ActiveChanged', (active: boolean) => { + this.#screensaverActive = active; + }); + } + ); + this.#lastActiveEventAt = Date.now(); const onActiveEvent = this.#onActiveEvent.bind(this); @@ -67,7 +95,7 @@ class ActiveWindowService { } isActive(): boolean { - if (this.#isFocused) { + if (this.#isFocused && !this.#screensaverActive) { return Date.now() < this.#lastActiveEventAt + ACTIVE_TIMEOUT; } @@ -77,9 +105,7 @@ class ActiveWindowService { } return ( - Date.now() < - this.#lastActiveNonFocusingEventAt + - ACTIVE_AFTER_NON_FOCUSING_EVENT_TIMEOUT + !this.#screensaverActive ); } From c4c2397364a54e27412770585de64fedd4ae8d79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Bellegarde?= Date: Sat, 2 Aug 2025 16:35:49 +0200 Subject: [PATCH 06/20] app: Add D-Bus interface and wrapper to speed up secondary instance handling When Signal is minimized to the system tray, launching a second instance causes Electron to go through a full startup cycle before signaling the main instance to show the window. This results in a noticeable delay (~2 seconds). To avoid this, a lightweight D-Bus interface (`org.signal.Signal`) has been added. It exposes a `ShowWindow` method to allow the window to be shown immediately without full app startup. A wrapper script (`signal-desktop-mobile`) now attempts to call this method via `gdbus`. If the main instance is not running, it falls back to launching a new Electron process. This approach significantly reduces perceived startup time when the app is already running but hidden. --- app/main.main.ts | 44 +++++++++++++++++++++++++++++++++++++++++++ signal-desktop-mobile | 3 +++ 2 files changed, 47 insertions(+) create mode 100644 signal-desktop-mobile diff --git a/app/main.main.ts b/app/main.main.ts index 4ffa78639..90ec71f31 100644 --- a/app/main.main.ts +++ b/app/main.main.ts @@ -7,6 +7,7 @@ import * as os from 'node:os'; import fsExtra from 'fs-extra'; import { randomBytes } from 'node:crypto'; import { createParser } from 'dashdash'; +import dbus from 'dbus-native'; import fastGlob from 'fast-glob'; import PQueue from 'p-queue'; @@ -177,6 +178,47 @@ function getMainWindow() { return mainWindow; } +function setupDbus() { + const sessionBus = dbus.sessionBus(); + + if (!sessionBus) { + log.error('Could not connect to D-Bus session bus.'); + return; + } + + const serviceName = 'org.signal.Signal'; + const objectPath = '/org/signal/Signal'; + const interfaceName = 'org.signal.Signal'; + + sessionBus.requestName(serviceName, 0x4, (err, retCode) => { + if (err) { + log.error('Failed to request D-Bus name:', err); + return; + } + if (retCode !== 1) { + log.error('D-Bus name already taken.'); + return; + } + + const obj = { + 'org.signal.Signal': { + ShowWindow(callback: () => void) { + showWindow(); + }, + }, + }; + + sessionBus.exportInterface(obj['org.signal.Signal'], objectPath, { + name: interfaceName, + methods: { + ShowWindow: ['', '', [], []], + }, + signals: {}, + properties: {}, + }); + }); +} + const development = getEnvironment() === Environment.Development || getEnvironment() === Environment.Staging; @@ -784,6 +826,8 @@ async function createWindow() { systemTrayService.setMainWindow(mainWindow); } + setupDbus(); + function saveWindowStats() { if (!windowConfig) { return; diff --git a/signal-desktop-mobile b/signal-desktop-mobile new file mode 100644 index 000000000..5350b82ce --- /dev/null +++ b/signal-desktop-mobile @@ -0,0 +1,3 @@ +#!/bin/sh + +gdbus call -e -d org.signal.Signal -o /org/signal/Signal -m org.signal.Signal.ShowWindow >/dev/null 2>&1|| /usr/lib/signal-desktop/signal-desktop --enable-features=UseOzonePlatform --ozone-platform=wayland --enable-wayland-ime --wayland-text-input-version=3 $@ From e81a619f21835ae5817a91703d8c14fd6ccf62ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Bellegarde?= Date: Wed, 5 Nov 2025 18:24:49 +0100 Subject: [PATCH 07/20] ts: util: buildExperiation: Never expire --- ts/util/buildExpiration.std.ts | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/ts/util/buildExpiration.std.ts b/ts/util/buildExpiration.std.ts index 530443ec7..693d6e4db 100644 --- a/ts/util/buildExpiration.std.ts +++ b/ts/util/buildExpiration.std.ts @@ -59,30 +59,5 @@ export function hasBuildExpired({ now, logger, }: HasBuildExpiredOptionsType): boolean { - if ( - getEnvironment() !== Environment.PackagedApp && - buildExpirationTimestamp === 0 - ) { - return false; - } - - if (isInPast(buildExpirationTimestamp)) { - return true; - } - - const safeExpirationMs = autoDownloadUpdate - ? NINETY_ONE_DAYS - : THIRTY_ONE_DAYS; - - const buildExpirationDuration = buildExpirationTimestamp - now; - const tooFarIntoFuture = buildExpirationDuration > safeExpirationMs; - - if (tooFarIntoFuture) { - logger.error( - 'Build expiration is set too far into the future', - buildExpirationTimestamp - ); - } - - return tooFarIntoFuture || isInPast(buildExpirationTimestamp); + return false; } From 8284d3e12f2eac27b4338a5097490dea5211031d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Bellegarde?= Date: Wed, 5 Nov 2025 18:25:56 +0100 Subject: [PATCH 08/20] ts: services: notifications: Show one notification per conversation --- ts/services/notifications.preload.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/ts/services/notifications.preload.ts b/ts/services/notifications.preload.ts index 03ad822da..e6d9c5eba 100644 --- a/ts/services/notifications.preload.ts +++ b/ts/services/notifications.preload.ts @@ -82,7 +82,6 @@ class NotificationService extends EventEmitter { public isEnabled = false; - #lastNotification: null | Notification = null; #notificationData: null | NotificationDataType = null; #tokenData: { token: string; data: NotificationClickData } | undefined; @@ -192,13 +191,11 @@ class NotificationService extends EventEmitter { }) ); } else { - this.#lastNotification?.close(); - const notification = new window.Notification(title, { body: OS.isLinux() ? filterNotificationText(message) : message, icon: iconUrl, silent: true, - tag: messageId, + tag: conversationId }); notification.onclick = () => { @@ -230,8 +227,6 @@ class NotificationService extends EventEmitter { throw missingCaseError(type); } }; - - this.#lastNotification = notification; } if (!silent) { @@ -344,9 +339,6 @@ class NotificationService extends EventEmitter { this.#tokenData = undefined; drop(window.IPC.clearAllWindowsNotifications()); } - } else if (this.#lastNotification) { - this.#lastNotification.close(); - this.#lastNotification = null; } // This isn't a boolean because TypeScript isn't smart enough to know that, if From 9162ead8124a16a1077ca499e2f15567cb7ffdf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Bellegarde?= Date: Wed, 5 Nov 2025 18:35:08 +0100 Subject: [PATCH 09/20] ts: components: fun: Disable autofocus --- ts/components/fun/base/FunSearch.dom.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/components/fun/base/FunSearch.dom.tsx b/ts/components/fun/base/FunSearch.dom.tsx index 6f0cb1b87..f15ce63e2 100644 --- a/ts/components/fun/base/FunSearch.dom.tsx +++ b/ts/components/fun/base/FunSearch.dom.tsx @@ -53,7 +53,7 @@ export function FunSearch(props: FunSearchProps): React.JSX.Element { onBlur={handleBlur} placeholder={props.placeholder} // eslint-disable-next-line jsx-a11y/no-autofocus - autoFocus={shouldAutoFocus} + autoFocus={false} /> {props.searchInput !== '' && (
From ec0524b90f34bc0c37c5de7702db6ad3fdfc8e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Bellegarde?= Date: Wed, 4 Dec 2024 14:16:23 +0100 Subject: [PATCH 17/20] circleci: Initial config --- .circleci/config.yml | 26 ++++++++++++++++++++++++++ debian/changelog | 5 +++++ debian/control | 21 +++++++++++++++++++++ debian/rules | 34 ++++++++++++++++++++++++++++++++++ debian/source/format | 1 + 5 files changed, 87 insertions(+) create mode 100644 .circleci/config.yml create mode 100644 debian/changelog create mode 100644 debian/control create mode 100755 debian/rules create mode 100644 debian/source/format diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000..5285e5390 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,26 @@ +version: 2.1 + +setup: true + +orbs: + continuation: circleci/continuation@0.1.2 + droidian-buildd: droidian-releng/droidian-buildd-orb@volatile + +jobs: + setup: + executor: continuation/default + resource_class: small + steps: + - droidian-buildd/checkout + - droidian-buildd/generate + - continuation/continue: + configuration_path: generated_config.yml + +workflows: + setup: + jobs: + - setup: + filters: + tags: + only: /^droidian\/.*\/.*/ + diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 000000000..3d73598b8 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +signal-desktop (7.35-1) unstable; urgency=medium + + * Initial release. + + -- Cédric Bellegarde Fri, 10 May 2024 19:10:29 +0100 diff --git a/debian/control b/debian/control new file mode 100644 index 000000000..c37c0b916 --- /dev/null +++ b/debian/control @@ -0,0 +1,21 @@ +Source: signal-desktop +Section: utils +Priority: optional +Maintainer: Cédric Bellegarde +Rules-Requires-Root: no +Build-Depends: + debhelper-compat (= 13), + build-essential, + nodejs, + npm, + git +Standards-Version: 4.6.2 +Homepage: https://github.com/bellegarde-c/Signal-Desktop + +Package: signal-desktop +Architecture: any +Depends: + dbus, + ${shlibs:Depends}, + ${misc:Depends}, +Description: A private messenger for Windows, macOS, and Linux. diff --git a/debian/rules b/debian/rules new file mode 100755 index 000000000..2682f28db --- /dev/null +++ b/debian/rules @@ -0,0 +1,34 @@ +#!/usr/bin/make -f + +# See debhelper(7) (uncomment to enable). +# Output every command that modifies files on the build system. +#export DH_VERBOSE = 1 + + +# See FEATURE AREAS in dpkg-buildflags(1). +#export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +# See ENVIRONMENT in dpkg-buildflags(1). +# Package maintainers to append CFLAGS. +#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic +# Package maintainers to append LDFLAGS. +#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed + +override_dh_auto_build: + git lfs install + sed 's#"node": "#&>=#' -i package.json + npm --prefix ./sticker-creator/ install + npm install --ignore-engines + npm --prefix ./sticker-creator/ run build + npm run build + ls -l + +%: + dh $@ + + +# dh_make generated override targets. +# This is an example for Cmake (see ). +#override_dh_auto_configure: +# dh_auto_configure -- \ +# -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 000000000..163aaf8d8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) From 2c41255ef78b117e9ac16b8e68b5228697f1cf92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Bellegarde?= Date: Thu, 5 Dec 2024 09:46:17 +0100 Subject: [PATCH 18/20] debian: Create package --- debian/control | 16 +++++++++++++--- debian/rules | 27 ++++++++++++++++++++++----- package.json | 16 +--------------- signal-desktop.desktop | 12 ++++++++++++ 4 files changed, 48 insertions(+), 23 deletions(-) create mode 100644 signal-desktop.desktop diff --git a/debian/control b/debian/control index c37c0b916..53ca58dc0 100644 --- a/debian/control +++ b/debian/control @@ -8,14 +8,24 @@ Build-Depends: build-essential, nodejs, npm, - git + git, + git-lfs, + libnotify4, + libxtst6, + libnss3, + libasound2t64, + libpulse0, + libxss1, + libc6 (>= 2.31), + libgtk-3-0, + libgbm1, + libx11-xcb1 Standards-Version: 4.6.2 -Homepage: https://github.com/bellegarde-c/Signal-Desktop +Homepage: https://github.com/droidian/Signal-Desktop Package: signal-desktop Architecture: any Depends: - dbus, ${shlibs:Depends}, ${misc:Depends}, Description: A private messenger for Windows, macOS, and Linux. diff --git a/debian/rules b/debian/rules index 2682f28db..b3834e136 100755 --- a/debian/rules +++ b/debian/rules @@ -14,14 +14,31 @@ # Package maintainers to append LDFLAGS. #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed +export PATH := /tmp/signal-desktop/node_modules/.bin:$(PATH) + +override_dh_dwz: + +override_dh_shlibdeps: + dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info + + override_dh_auto_build: git lfs install sed 's#"node": "#&>=#' -i package.json - npm --prefix ./sticker-creator/ install - npm install --ignore-engines - npm --prefix ./sticker-creator/ run build - npm run build - ls -l + npm install --prefix /tmp/signal-desktop pnpm + pnpm install --dir ./sticker-creator/ + pnpm install + pnpm run build + +override_dh_auto_install: + install -d debian/signal-desktop/usr/bin + install -d debian/signal-desktop/usr/lib + mv release/linux-unpacked debian/signal-desktop/usr/lib/signal-desktop || true + mv release/linux-arm64-unpacked debian/signal-desktop/usr/lib/signal-desktop || true + ln -s /usr/lib/signal-desktop/signal-desktop debian/signal-desktop/usr/bin/signal-desktop-mobile + chmod u+s debian/signal-desktop/usr/lib/signal-desktop/chrome-sandbox + for i in 16 24 32 48 64 128 256 512 1024; do install -Dm 644 "build/icons/png/$${i}x$${i}.png" "debian/signal-desktop/usr/share/icons/hicolor/$${i}x$${i}/apps/signal-desktop.png"; done + install -Dm 644 ./signal-desktop.desktop -t debian/signal-desktop/usr/share/applications %: dh $@ diff --git a/package.json b/package.json index a6ab6cd97..42a1d6d6a 100644 --- a/package.json +++ b/package.json @@ -528,7 +528,7 @@ }, "artifactName": "${name}_${version}_${arch}.${ext}", "target": [ - "deb" + "dir" ], "icon": "build/icons/png", "publish": [ @@ -552,20 +552,6 @@ } ] }, - "deb": { - "depends": [ - "libnotify4", - "libxtst6", - "libnss3", - "libasound2", - "libpulse0", - "libxss1", - "libc6 (>= 2.34)", - "libgtk-3-0", - "libgbm1", - "libx11-xcb1" - ] - }, "protocols": { "name": "sgnl-url-scheme", "schemes": [ diff --git a/signal-desktop.desktop b/signal-desktop.desktop new file mode 100644 index 000000000..6104ca923 --- /dev/null +++ b/signal-desktop.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Type=Application +Name=Signal +Comment=Signal Messenger +Icon=signal-desktop +Exec=/usr/bin/signal-desktop-mobile --enable-features=UseOzonePlatform --ozone-platform=wayland --enable-wayland-ime --wayland-text-input-version=3 -- %u +Terminal=false +Categories=Network;InstantMessaging; +StartupWMClass=signal +MimeType=x-scheme-handler/sgnl;x-scheme-handler/signalcaptcha; +Keywords=sgnl;chat;im;messaging;messenger;security;privat; +X-GNOME-UsesNotifications=true From 5dc4f0937b291083a8ddd0b838cd21129b2ec057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Bellegarde?= Date: Wed, 11 Mar 2026 22:30:56 +0100 Subject: [PATCH 19/20] debian: Update desktop file --- debian/rules | 2 +- signal-desktop.desktop | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/rules b/debian/rules index b3834e136..e5df58bdf 100755 --- a/debian/rules +++ b/debian/rules @@ -35,10 +35,10 @@ override_dh_auto_install: install -d debian/signal-desktop/usr/lib mv release/linux-unpacked debian/signal-desktop/usr/lib/signal-desktop || true mv release/linux-arm64-unpacked debian/signal-desktop/usr/lib/signal-desktop || true - ln -s /usr/lib/signal-desktop/signal-desktop debian/signal-desktop/usr/bin/signal-desktop-mobile chmod u+s debian/signal-desktop/usr/lib/signal-desktop/chrome-sandbox for i in 16 24 32 48 64 128 256 512 1024; do install -Dm 644 "build/icons/png/$${i}x$${i}.png" "debian/signal-desktop/usr/share/icons/hicolor/$${i}x$${i}/apps/signal-desktop.png"; done install -Dm 644 ./signal-desktop.desktop -t debian/signal-desktop/usr/share/applications + install -Dm 755 ./signal-desktop-mobile -t debian/signal-desktop/usr/bin %: dh $@ diff --git a/signal-desktop.desktop b/signal-desktop.desktop index 6104ca923..424b72e52 100644 --- a/signal-desktop.desktop +++ b/signal-desktop.desktop @@ -3,7 +3,7 @@ Type=Application Name=Signal Comment=Signal Messenger Icon=signal-desktop -Exec=/usr/bin/signal-desktop-mobile --enable-features=UseOzonePlatform --ozone-platform=wayland --enable-wayland-ime --wayland-text-input-version=3 -- %u +Exec=/usr/bin/signal-desktop-mobile %u Terminal=false Categories=Network;InstantMessaging; StartupWMClass=signal From f79bc0b2b8ef493c5661404efa9bbe2152f3f5bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2026 21:43:14 +0000 Subject: [PATCH 20/20] Bump the minor-actions-dependencies group across 1 directory with 2 updates Bumps the minor-actions-dependencies group with 2 updates in the / directory: [pnpm/action-setup](https://github.com/pnpm/action-setup) and [actions/setup-node](https://github.com/actions/setup-node). Updates `pnpm/action-setup` from 4.2.0 to 4.3.0 - [Release notes](https://github.com/pnpm/action-setup/releases) - [Commits](https://github.com/pnpm/action-setup/compare/41ff72655975bd51cab0327fa583b6e92b6d3061...b906affcce14559ad1aafd4ab0e942779e9f58b1) Updates `actions/setup-node` from 6.0.0 to 6.3.0 - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/2028fbc5c25fe9cf00d9f06a71cc4710d4507903...53b83947a5a98c8d113130e565377fae1a50d02f) --- updated-dependencies: - dependency-name: pnpm/action-setup dependency-version: 4.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-actions-dependencies - dependency-name: actions/setup-node dependency-version: 6.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-actions-dependencies ... Signed-off-by: dependabot[bot] --- .github/workflows/benchmark.yml | 4 ++-- .github/workflows/ci.yml | 28 ++++++++++++++-------------- .github/workflows/danger.yml | 4 ++-- .github/workflows/icu-book.yml | 4 ++-- .github/workflows/stories.yml | 4 ++-- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index de8b35d56..4de7c5f05 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -83,9 +83,9 @@ jobs: # key: sccache-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml', 'patches/**') }} - name: Setup pnpm - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 - name: Setup node.js - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 with: node-version-file: '.nvmrc' cache: 'pnpm' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1549510c..1a58b3d3e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,9 +20,9 @@ jobs: - run: uname -a - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - name: Setup pnpm - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 - name: Setup node.js - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 with: node-version-file: '.nvmrc' cache: 'pnpm' @@ -89,9 +89,9 @@ jobs: - run: uname -a - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - name: Setup pnpm - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 - name: Setup node.js - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 with: node-version-file: '.nvmrc' cache: 'pnpm' @@ -163,9 +163,9 @@ jobs: - run: uname -a - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - name: Setup pnpm - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 - name: Setup node.js - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 with: node-version-file: '.nvmrc' cache: 'pnpm' @@ -266,9 +266,9 @@ jobs: - run: git config --global core.eol lf - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - name: Setup pnpm - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 - name: Setup node.js - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 with: node-version-file: '.nvmrc' cache: 'pnpm' @@ -337,9 +337,9 @@ jobs: steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - name: Setup pnpm - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 - name: Setup node.js - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 with: node-version-file: '.nvmrc' @@ -383,9 +383,9 @@ jobs: uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - name: Setup pnpm - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 - name: Setup node.js - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 with: node-version-file: '.nvmrc' cache: 'pnpm' @@ -478,9 +478,9 @@ jobs: - run: uname -a - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - name: Setup pnpm - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 - name: Setup node.js - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 with: node-version-file: '.nvmrc' cache: 'pnpm' diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml index d86d38434..6d4c7a2e7 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/danger.yml @@ -14,9 +14,9 @@ jobs: with: fetch-depth: 0 # fetch all history - name: Setup pnpm - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 - name: Setup node.js - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 with: node-version-file: '.nvmrc' package-manager-cache: false # Avoid cache key clashes diff --git a/.github/workflows/icu-book.yml b/.github/workflows/icu-book.yml index f38956049..32b357120 100644 --- a/.github/workflows/icu-book.yml +++ b/.github/workflows/icu-book.yml @@ -15,9 +15,9 @@ jobs: steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - name: Setup pnpm - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 - name: Setup node.js - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 with: node-version-file: '.nvmrc' cache: 'pnpm' diff --git a/.github/workflows/stories.yml b/.github/workflows/stories.yml index 14abc1d43..15c84d416 100644 --- a/.github/workflows/stories.yml +++ b/.github/workflows/stories.yml @@ -15,9 +15,9 @@ jobs: steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - name: Setup pnpm - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 - name: Setup node.js - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 with: node-version-file: '.nvmrc' cache: 'pnpm'