diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000..5285e53904 --- /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/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index de8b35d562..96015fe16f 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -137,7 +137,7 @@ jobs: - name: Upload benchmark logs on failure if: failure() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: logs path: artifacts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1549510c6..1b2e2c756d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -149,7 +149,7 @@ jobs: - name: Upload artifacts on failure if: failure() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: path: artifacts @@ -251,7 +251,7 @@ jobs: - name: Upload artifacts on failure if: failure() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: path: artifacts @@ -321,7 +321,7 @@ jobs: - name: Upload artifacts on failure if: failure() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: path: artifacts @@ -458,7 +458,7 @@ jobs: - name: Upload mock server test logs on failure if: failure() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: logs-${{ matrix.workerIndex }} path: artifacts diff --git a/.github/workflows/icu-book.yml b/.github/workflows/icu-book.yml index f38956049c..25f8ab6d1e 100644 --- a/.github/workflows/icu-book.yml +++ b/.github/workflows/icu-book.yml @@ -55,7 +55,7 @@ jobs: - name: Upload test artifacts if: github.event_name == 'workflow_dispatch' - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: desktop-test-icu path: stories @@ -63,7 +63,7 @@ jobs: - name: Upload release artifacts if: github.event_name != 'workflow_dispatch' - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: desktop-${{ github.ref_name }}-icu path: stories diff --git a/app/main.main.ts b/app/main.main.ts index a906a6beb0..71b7c7d033 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'; @@ -81,21 +82,10 @@ import * as bounce from '../ts/services/bounce.main.js'; import * as updater from '../ts/updater/index.main.js'; import { updateDefaultSession } from './updateDefaultSession.main.js'; import { PreventDisplaySleepService } from './PreventDisplaySleepService.std.js'; -import { - SystemTrayService, - focusAndForceToTop, -} from './SystemTrayService.main.js'; import { SystemTraySettingCache } from './SystemTraySettingCache.node.js'; import { OptionalResourceService } from './OptionalResourceService.main.js'; import { EmojiService } from './EmojiService.main.js'; import { - SystemTraySetting, - shouldMinimizeToSystemTray, - parseSystemTraySetting, -} from '../ts/types/SystemTraySetting.std.js'; -import { - getDefaultSystemTraySetting, - isSystemTraySupported, isContentProtectionEnabledByDefault, } from '../ts/types/Settings.std.js'; import * as ephemeralConfig from './ephemeral_config.main.js'; @@ -177,6 +167,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; @@ -244,15 +275,7 @@ function showWindow() { return; } - // Using focus() instead of show() seems to be important on Windows when our window - // has been docked using Aero Snap/Snap Assist. A full .show() call here will cause - // the window to reposition: - // https://github.com/signalapp/Signal-Desktop/issues/1429 - if (mainWindow.isVisible()) { - focusAndForceToTop(mainWindow); - } else { - mainWindow.show(); - } + mainWindow.show(); } if (!process.mas) { @@ -263,20 +286,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) { @@ -416,12 +426,6 @@ const zoomFactorService = new ZoomFactorService({ }, }); -let systemTrayService: SystemTrayService | undefined; -const systemTraySettingCache = new SystemTraySettingCache( - ephemeralConfig, - process.argv -); - const windowFromUserConfig = userConfig.get('window'); const windowFromEphemeral = ephemeralConfig.get('window'); export const windowConfigSchema = z.object({ @@ -635,14 +639,7 @@ if (OS.isWindows()) { windowIcon = join(__dirname, '../build/icons/png/512x512.png'); } -// The titlebar is hidden on: -// - Windows < 10 (7, 8) -// - macOS (but no custom titlebar is displayed, see -// `--title-bar-drag-area-height` in `stylesheets/_titlebar.scss` -const mainTitleBarStyle = OS.isMacOS() - ? ('hidden' as const) - : ('default' as const); - +const mainTitleBarStyle = 'hidden' as const; const nonMainTitleBarStyle = 'default' as const; async function safeLoadURL(window: BrowserWindow, url: string): Promise { @@ -691,21 +688,13 @@ async function createWindow() { ? Math.min(windowConfig.height, maxHeight) : DEFAULT_HEIGHT; - const [systemTraySetting, backgroundColor, spellcheck] = await Promise.all([ - systemTraySettingCache.get(), + const [backgroundColor, spellcheck] = await Promise.all([ isTestEnvironment(getEnvironment()) ? '#ffffff' // Tests should always be rendered on a white background : getBackgroundColor(), getSpellCheckSetting(), ]); - const startInTray = - isTestEnvironment(getEnvironment()) || - systemTraySetting === SystemTraySetting.MinimizeToAndStartInSystemTray; - - const shouldShowWindow = - !app.getLoginItemSettings().wasOpenedAsHidden && !startInTray; - const windowOptions: Electron.BrowserWindowConstructorOptions = { show: false, width, @@ -787,15 +776,8 @@ async function createWindow() { getResolvedMessagesLocale().i18n, log ); - if (!startInTray && windowConfig && windowConfig.maximized) { - mainWindow.maximize(); - } - if (!startInTray && windowConfig && windowConfig.fullscreen) { - mainWindow.setFullScreen(true); - } - if (systemTrayService) { - systemTrayService.setMainWindow(mainWindow); - } + + setupDbus(); function saveWindowStats() { if (!windowConfig) { @@ -871,113 +853,13 @@ async function createWindow() { readyForShutdown: windowState.readyForShutdown(), shouldQuit: windowState.shouldQuit(), }); - // If the application is terminating, just do the default - if ( - isTestEnvironment(getEnvironment()) || - (windowState.readyForShutdown() && windowState.shouldQuit()) - ) { - return; - } - // Prevent the shutdown e.preventDefault(); // Disable media playback mainWindow.webContents.send('set-media-playback-disabled', true); - // In certain cases such as during an active call, we ask the user to confirm close - // which includes shutdown, clicking X on MacOS or closing to tray. - let shouldClose = true; - try { - shouldClose = await maybeRequestCloseConfirmation(); - } catch (error) { - log.warn( - 'Error while requesting close confirmation.', - Errors.toLogFormat(error) - ); - } - if (!shouldClose) { - updater.onRestartCanceled(); - return; - } - - /** - * if the user is in fullscreen mode and closes the window, not the - * application, we need them leave fullscreen first before closing it to - * prevent a black screen. - * Also check for mainWindow because it might become undefined while - * waiting for close confirmation. - * - * issue: https://github.com/signalapp/Signal-Desktop/issues/4348 - */ - if (mainWindow) { - if (mainWindow.isFullScreen()) { - mainWindow.once('leave-full-screen', () => mainWindow?.hide()); - mainWindow.setFullScreen(false); - } else { - mainWindow.hide(); - } - } - - // On Mac, or on other platforms when the tray icon is in use, the window - // should be only hidden, not closed, when the user clicks the close button - const usingTrayIcon = shouldMinimizeToSystemTray( - await systemTraySettingCache.get() - ); - if ( - mainWindow && - !windowState.shouldQuit() && - (usingTrayIcon || OS.isMacOS()) - ) { - if (!usingTrayIcon) { - return; - } - - const shownTrayNotice = ephemeralConfig.get('shown-tray-notice'); - if (shownTrayNotice) { - log.info('close: not showing tray notice'); - return; - } - - ephemeralConfig.set('shown-tray-notice', true); - log.info('close: showing tray notice'); - - if (OS.isWindows()) { - showWindowsNotification({ - type: NotificationType.MinimizedToTray, - token: 'unused', - heading: getResolvedMessagesLocale().i18n( - 'icu:minimizeToTrayNotification--title' - ), - body: getResolvedMessagesLocale().i18n( - 'icu:minimizeToTrayNotification--body' - ), - }); - return; - } - - const n = new Notification({ - title: getResolvedMessagesLocale().i18n( - 'icu:minimizeToTrayNotification--title' - ), - body: getResolvedMessagesLocale().i18n( - 'icu:minimizeToTrayNotification--body' - ), - }); - - n.show(); - return; - } - - // Persist pending window settings to ephemeralConfig - debouncedSaveStats.flush(); - - windowState.markRequestedShutdown(); - await requestShutdown(); - windowState.markReadyForShutdown(); - - await sql.close(); - app.quit(); + mainWindow.hide(); }); // Emitted when the window is closed. @@ -990,9 +872,6 @@ async function createWindow() { if (settingsChannel) { settingsChannel.setMainWindow(mainWindow); } - if (systemTrayService) { - systemTrayService.setMainWindow(mainWindow); - } }); mainWindow.on('enter-full-screen', () => { @@ -1031,10 +910,7 @@ async function createWindow() { mainWindow.webContents.send('ci:event', 'db-initialized', {}); - if (shouldShowWindow) { - log.info('showing main window'); - mainWindow.show(); - } + mainWindow.show(); }; if (OS.isLinux() && OS.isWaylandEnabled()) { @@ -1998,23 +1874,7 @@ function loadPreferredSystemLocales(): Array { } async function getDefaultLoginItemSettings(): Promise { - if (!OS.isWindows()) { - return {}; - } - - const systemTraySetting = await systemTraySettingCache.get(); - if ( - systemTraySetting !== SystemTraySetting.MinimizeToSystemTray && - // This is true when we just started with `--start-in-tray` - systemTraySetting !== SystemTraySetting.MinimizeToAndStartInSystemTray - ) { - return {}; - } - - // The effect of this is that if both auto-launch and minimize to system tray - // are enabled on Windows - we will start the app in tray automatically, - // letting the Desktop shortcuts still start the Signal not in tray. - return { args: ['--start-in-tray'] }; + return {}; } // Signal doesn't really use media keys so we set this switch here to unblock @@ -2100,55 +1960,11 @@ app.on('ready', async () => { sqlInitPromise = initializeSQL(userDataPath); - // First run: configure Signal to minimize to tray. Additionally, on Windows - // enable auto-start with start-in-tray so that starting from a Desktop icon - // would still show the window. - // (User can change these settings later) - if ( - isSystemTraySupported(OS) && - (await systemTraySettingCache.get()) === SystemTraySetting.Uninitialized - ) { - const newValue = getDefaultSystemTraySetting(OS, app.getVersion()); - log.info(`app.ready: setting system-tray-setting to ${newValue}`); - systemTraySettingCache.set(newValue); - - ephemeralConfig.set('system-tray-setting', newValue); - - if (OS.isWindows()) { - log.info('app.ready: enabling open at login'); - app.setLoginItemSettings({ - ...(await getDefaultLoginItemSettings()), - openAtLogin: true, - }); - } - } - const startTime = Date.now(); settingsChannel = new SettingsChannel(); settingsChannel.install(); - settingsChannel.on('change:systemTraySetting', async rawSystemTraySetting => { - const { openAtLogin } = app.getLoginItemSettings( - await getDefaultLoginItemSettings() - ); - - const systemTraySetting = parseSystemTraySetting(rawSystemTraySetting); - systemTraySettingCache.set(systemTraySetting); - - if (systemTrayService) { - const isEnabled = shouldMinimizeToSystemTray(systemTraySetting); - systemTrayService.setEnabled(isEnabled); - } - - // Default login item settings might have changed, so update the object. - log.info('refresh-auto-launch: new value', openAtLogin); - app.setLoginItemSettings({ - ...(await getDefaultLoginItemSettings()), - openAtLogin, - }); - }); - settingsChannel.on( 'ephemeral-setting-changed', sendPreferencesChangedEventToWindows @@ -2261,6 +2077,7 @@ app.on('ready', async () => { nodeIntegration: false, sandbox: true, contextIsolation: true, + backgroundThrottling: false, preload: join(__dirname, '../bundles/loading/preload.preload.js'), }, icon: windowIcon, @@ -2347,14 +2164,6 @@ app.on('ready', async () => { setupMenu(); - systemTrayService = new SystemTrayService({ - i18n: resolvedTranslationsLocale.i18n, - }); - systemTrayService.setMainWindow(mainWindow); - systemTrayService.setEnabled( - shouldMinimizeToSystemTray(await systemTraySettingCache.get()) - ); - await ensureFilePermissions([ 'config.json', 'sql/db.sqlite', @@ -2556,7 +2365,6 @@ app.on('before-quit', e => { ...getWindowDebugInfo(), }); - systemTrayService?.markShouldQuit(); windowState.markShouldQuit(); }); @@ -2728,12 +2536,6 @@ ipc.on( } ); -ipc.on('update-tray-icon', (_event: Electron.Event, unreadCount: number) => { - if (systemTrayService) { - systemTrayService.setUnreadCount(unreadCount); - } -}); - // Debug Log-related IPC calls ipc.on( diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000000..3d73598b8d --- /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 0000000000..53ca58dc08 --- /dev/null +++ b/debian/control @@ -0,0 +1,31 @@ +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, + 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/droidian/Signal-Desktop + +Package: signal-desktop +Architecture: any +Depends: + ${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 0000000000..e5df58bdfe --- /dev/null +++ b/debian/rules @@ -0,0 +1,51 @@ +#!/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 + +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 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 + 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 $@ + + +# 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 0000000000..163aaf8d82 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/package.json b/package.json index 633ec1e109..42a1d6d6a7 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", @@ -527,7 +528,7 @@ }, "artifactName": "${name}_${version}_${arch}.${ext}", "target": [ - "deb" + "dir" ], "icon": "build/icons/png", "publish": [ @@ -551,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-mobile b/signal-desktop-mobile new file mode 100644 index 0000000000..5350b82ceb --- /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 $@ diff --git a/signal-desktop.desktop b/signal-desktop.desktop new file mode 100644 index 0000000000..424b72e521 --- /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 %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 diff --git a/stylesheets/components/Droidian.scss b/stylesheets/components/Droidian.scss new file mode 100644 index 0000000000..6b7af8fbc7 --- /dev/null +++ b/stylesheets/components/Droidian.scss @@ -0,0 +1,135 @@ +.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; +} + +.MediaEditor__tools--input { + width: 150px; +} \ No newline at end of file diff --git a/stylesheets/components/NavSidebar.scss b/stylesheets/components/NavSidebar.scss index ba5f39d483..21540684c4 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 953f4987e0..76983e70b7 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'; diff --git a/ts/background.preload.ts b/ts/background.preload.ts index 69109b074b..e25394ef92 100644 --- a/ts/background.preload.ts +++ b/ts/background.preload.ts @@ -330,6 +330,8 @@ export async function startApp(): Promise { storage: itemStorage, }); + notificationService.enable(); + await initializeMessageCounter(); // Initialize WebAPI as early as possible @@ -1932,7 +1934,6 @@ export async function startApp(): Promise { onDecryptionErrorQueue.pause(); onRetryRequestQueue.pause(); deliveryReceiptQueue.pause(); - notificationService.disable(); } // 2. After the socket finishes processing any queued messages, restart these queues @@ -1943,7 +1944,6 @@ export async function startApp(): Promise { onDecryptionErrorQueue.start(); onRetryRequestQueue.start(); deliveryReceiptQueue.start(); - notificationService.enable(); } function isSocketOnline() { diff --git a/ts/components/ChatsTab.dom.tsx b/ts/components/ChatsTab.dom.tsx index 636acc4826..62ec8d6152 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/CompositionArea.dom.tsx b/ts/components/CompositionArea.dom.tsx index f9dc0d340e..a077ea0589 100644 --- a/ts/components/CompositionArea.dom.tsx +++ b/ts/components/CompositionArea.dom.tsx @@ -456,6 +456,10 @@ export const CompositionArea = memo(function CompositionArea({ } }, []); + const launchRecorder = useCallback(() => { + startRecording(conversationId); + }, []); + const launchMediaPicker = useCallback( () => launchAttachmentPicker('media'), [launchAttachmentPicker] @@ -785,18 +789,6 @@ export const CompositionArea = memo(function CompositionArea({ ); - const micButtonFragment = shouldShowMicrophone ? ( -
- -
- ) : null; - const editMessageFragment = draftEditMessage ? ( <> {large &&
} @@ -839,6 +831,9 @@ export const CompositionArea = memo(function CompositionArea({
+ + {i18n('icu:Keyboard--begin-recording-voice-note')} + {i18n('icu:CompositionArea__AttachMenu__PhotosAndVideos')} @@ -1287,7 +1282,6 @@ export const CompositionArea = memo(function CompositionArea({ )} {!isViewOnceActive && !large && ( <> - {!dirty ? micButtonFragment : null} {editMessageFragment} {composerAddMenuButton} @@ -1302,7 +1296,6 @@ export const CompositionArea = memo(function CompositionArea({ > {leftHandSideButtonsFragment} {composerAddMenuButton} - {!dirty ? micButtonFragment : null} {editMessageFragment} {dirty || !shouldShowMicrophone ? sendButtonFragment : null}
diff --git a/ts/components/MediaEditor.dom.tsx b/ts/components/MediaEditor.dom.tsx index fad8cc12f2..83c32bb4fb 100644 --- a/ts/components/MediaEditor.dom.tsx +++ b/ts/components/MediaEditor.dom.tsx @@ -810,7 +810,7 @@ export function MediaEditor({ }); rect.on('deselected', () => { - setEditMode(undefined); + fabricCanvas.setActiveObject(rect); }); fabricCanvas.add(rect); diff --git a/ts/components/NavSidebar.dom.tsx b/ts/components/NavSidebar.dom.tsx index 42e3b9829e..14cc780d56 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 8f68574263..c5afb4ad9d 100644 --- a/ts/components/Preferences.dom.tsx +++ b/ts/components/Preferences.dom.tsx @@ -2338,7 +2338,7 @@ export function Preferences({
{ 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 ( -
-
+
+
); } diff --git a/ts/components/conversation/ConversationHeader.dom.tsx b/ts/components/conversation/ConversationHeader.dom.tsx index dbec843b98..190ba4de33 100644 --- a/ts/components/conversation/ConversationHeader.dom.tsx +++ b/ts/components/conversation/ConversationHeader.dom.tsx @@ -12,6 +12,7 @@ import { } from '../../hooks/useKeyboardShortcuts.dom.js'; import { SizeObserver } from '../../hooks/useSizeObserver.dom.js'; import type { ConversationTypeType } from '../../state/ducks/conversations.preload.js'; +import { showConversation } from '../../state/ducks/conversations.preload.js'; import type { HasStories } from '../../types/Stories.std.js'; import type { LocalizerType, ThemeType } from '../../types/Util.std.js'; import type { DurationInSeconds } from '../../util/durations/index.std.js'; @@ -53,6 +54,8 @@ import type { } from '../../state/selectors/timeline.preload.js'; import { tw } from '../../axo/tw.dom.js'; +import { useDispatch } from 'react-redux'; + function HeaderInfoTitle({ name, title, @@ -483,6 +486,11 @@ function HeaderContent({ onViewUserStories: () => 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/components/fun/base/FunSearch.dom.tsx b/ts/components/fun/base/FunSearch.dom.tsx index 6f0cb1b873..f15ce63e21 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 !== '' && (