Titlebar fixes
This commit is contained in:
parent
f273333046
commit
f92be05b15
41 changed files with 225 additions and 154 deletions
|
@ -9,6 +9,9 @@
|
||||||
type="text/css"
|
type="text/css"
|
||||||
/>
|
/>
|
||||||
<script>
|
<script>
|
||||||
|
// eslint-disable-next-line
|
||||||
|
const noop = () => {};
|
||||||
|
|
||||||
window.SignalWindow = window.SignalWindow || {};
|
window.SignalWindow = window.SignalWindow || {};
|
||||||
window.SignalWindow.log = {
|
window.SignalWindow.log = {
|
||||||
fatal: console.error.bind(console),
|
fatal: console.error.bind(console),
|
||||||
|
@ -19,19 +22,27 @@
|
||||||
trace: console.trace.bind(console),
|
trace: console.trace.bind(console),
|
||||||
};
|
};
|
||||||
window.SignalContext = {
|
window.SignalContext = {
|
||||||
|
activeWindowService: {
|
||||||
|
isActive: () => true;
|
||||||
|
registerForActive: noop,
|
||||||
|
unregisterForActive: noop,
|
||||||
|
registerForChange: noop,
|
||||||
|
unregisterForChange: noop,
|
||||||
|
},
|
||||||
|
|
||||||
nativeThemeListener: {
|
nativeThemeListener: {
|
||||||
getSystemValue: async () => 'light',
|
getSystemValue: async () => 'light',
|
||||||
subscribe: () => {},
|
subscribe: noop,
|
||||||
unsubscribe: () => {},
|
unsubscribe: noop,
|
||||||
},
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
themeSetting: {
|
themeSetting: {
|
||||||
getValue: async () => 'light',
|
getValue: async () => 'light',
|
||||||
},
|
},
|
||||||
waitForChange: () => {},
|
waitForChange: noop,
|
||||||
},
|
},
|
||||||
OS: {
|
OS: {
|
||||||
isWindows11: () => false,
|
hasCustomTitleBar: () => false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -34,6 +34,7 @@ if (getEnvironment() === Environment.Production) {
|
||||||
process.env.SUPPRESS_NO_CONFIG_WARNING = '';
|
process.env.SUPPRESS_NO_CONFIG_WARNING = '';
|
||||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '';
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '';
|
||||||
process.env.SIGNAL_ENABLE_HTTP = '';
|
process.env.SIGNAL_ENABLE_HTTP = '';
|
||||||
|
process.env.CUSTOM_TITLEBAR = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// We load config after we've made our modifications to NODE_ENV
|
// We load config after we've made our modifications to NODE_ENV
|
||||||
|
|
28
app/main.ts
28
app/main.ts
|
@ -432,6 +432,7 @@ async function prepareUrl(
|
||||||
|
|
||||||
// Only used by the main window
|
// Only used by the main window
|
||||||
isMainWindowFullScreen: Boolean(mainWindow?.isFullScreen()),
|
isMainWindowFullScreen: Boolean(mainWindow?.isFullScreen()),
|
||||||
|
isMainWindowMaximized: Boolean(mainWindow?.isMaximized()),
|
||||||
|
|
||||||
// Only for tests
|
// Only for tests
|
||||||
argv: JSON.stringify(process.argv),
|
argv: JSON.stringify(process.argv),
|
||||||
|
@ -499,6 +500,17 @@ function handleCommonWindowEvents(
|
||||||
activeWindows.add(window);
|
activeWindows.add(window);
|
||||||
window.on('closed', () => activeWindows.delete(window));
|
window.on('closed', () => activeWindows.delete(window));
|
||||||
|
|
||||||
|
const setWindowFocus = () => {
|
||||||
|
window.webContents.send('set-window-focus', window.isFocused());
|
||||||
|
};
|
||||||
|
window.on('focus', setWindowFocus);
|
||||||
|
window.on('blur', setWindowFocus);
|
||||||
|
|
||||||
|
window.once('ready-to-show', setWindowFocus);
|
||||||
|
// This is a fallback in case we drop an event for some reason.
|
||||||
|
const focusInterval = setInterval(setWindowFocus, 10000);
|
||||||
|
window.on('closed', () => clearInterval(focusInterval));
|
||||||
|
|
||||||
// Works only for mainWindow because it has `enablePreferredSizeMode`
|
// Works only for mainWindow because it has `enablePreferredSizeMode`
|
||||||
let lastZoomFactor = window.webContents.getZoomFactor();
|
let lastZoomFactor = window.webContents.getZoomFactor();
|
||||||
const onZoomChanged = () => {
|
const onZoomChanged = () => {
|
||||||
|
@ -600,12 +612,12 @@ const mainTitleBarStyle =
|
||||||
? ('default' as const)
|
? ('default' as const)
|
||||||
: ('hidden' as const);
|
: ('hidden' as const);
|
||||||
|
|
||||||
const nonMainTitleBarStyle = OS.isWindows()
|
const nonMainTitleBarStyle = OS.hasCustomTitleBar()
|
||||||
? ('hidden' as const)
|
? ('hidden' as const)
|
||||||
: ('default' as const);
|
: ('default' as const);
|
||||||
|
|
||||||
async function getTitleBarOverlay(): Promise<TitleBarOverlayOptions | false> {
|
async function getTitleBarOverlay(): Promise<TitleBarOverlayOptions | false> {
|
||||||
if (!OS.isWindows()) {
|
if (!OS.hasCustomTitleBar()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -782,18 +794,6 @@ async function createWindow() {
|
||||||
mainWindow.on('resize', captureWindowStats);
|
mainWindow.on('resize', captureWindowStats);
|
||||||
mainWindow.on('move', captureWindowStats);
|
mainWindow.on('move', captureWindowStats);
|
||||||
|
|
||||||
const setWindowFocus = () => {
|
|
||||||
if (!mainWindow) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mainWindow.webContents.send('set-window-focus', mainWindow.isFocused());
|
|
||||||
};
|
|
||||||
mainWindow.on('focus', setWindowFocus);
|
|
||||||
mainWindow.on('blur', setWindowFocus);
|
|
||||||
mainWindow.once('ready-to-show', setWindowFocus);
|
|
||||||
// This is a fallback in case we drop an event for some reason.
|
|
||||||
setInterval(setWindowFocus, 10000);
|
|
||||||
|
|
||||||
if (getEnvironment() === Environment.Test) {
|
if (getEnvironment() === Environment.Test) {
|
||||||
mainWindow.loadURL(await prepareFileUrl([__dirname, '../test/index.html']));
|
mainWindow.loadURL(await prepareFileUrl([__dirname, '../test/index.html']));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -16,15 +16,13 @@ import type { ExecuteMenuRoleType } from '../../ts/components/TitleBarContainer'
|
||||||
import { useTheme } from '../../ts/hooks/useTheme';
|
import { useTheme } from '../../ts/hooks/useTheme';
|
||||||
|
|
||||||
export type AppPropsType = Readonly<{
|
export type AppPropsType = Readonly<{
|
||||||
platform: string;
|
|
||||||
executeMenuRole: ExecuteMenuRoleType;
|
executeMenuRole: ExecuteMenuRoleType;
|
||||||
isWindows11: boolean;
|
hasCustomTitleBar: boolean;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export const App = ({
|
export const App = ({
|
||||||
platform,
|
|
||||||
executeMenuRole,
|
executeMenuRole,
|
||||||
isWindows11,
|
hasCustomTitleBar,
|
||||||
}: AppPropsType): JSX.Element => {
|
}: AppPropsType): JSX.Element => {
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
@ -32,8 +30,7 @@ export const App = ({
|
||||||
return (
|
return (
|
||||||
<TitleBarContainer
|
<TitleBarContainer
|
||||||
iconSrc="../../images/icon_32.png"
|
iconSrc="../../images/icon_32.png"
|
||||||
platform={platform}
|
hasCustomTitleBar={hasCustomTitleBar}
|
||||||
isWindows11={isWindows11}
|
|
||||||
theme={theme}
|
theme={theme}
|
||||||
executeMenuRole={executeMenuRole}
|
executeMenuRole={executeMenuRole}
|
||||||
>
|
>
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
|
|
||||||
.facade {
|
.facade {
|
||||||
background: rgba(0, 0, 0, 0.33);
|
background: rgba(0, 0, 0, 0.33);
|
||||||
width: 100vw;
|
width: var(--window-width);
|
||||||
height: var(--window-height);
|
height: var(--window-height);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
left: var(--window-border);
|
||||||
top: 0;
|
top: var(--titlebar-height);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,7 @@ const ColdRoot = () => (
|
||||||
<I18n messages={localeMessages} locale={SignalContext.config.locale}>
|
<I18n messages={localeMessages} locale={SignalContext.config.locale}>
|
||||||
<App
|
<App
|
||||||
executeMenuRole={SignalContext.executeMenuRole}
|
executeMenuRole={SignalContext.executeMenuRole}
|
||||||
platform={SignalContext.OS.platform}
|
hasCustomTitleBar={SignalContext.OS.hasCustomTitleBar()}
|
||||||
isWindows11={SignalContext.OS.isWindows11()}
|
|
||||||
/>
|
/>
|
||||||
</I18n>
|
</I18n>
|
||||||
</Router>
|
</Router>
|
||||||
|
|
|
@ -672,11 +672,11 @@
|
||||||
@mixin install-screen {
|
@mixin install-screen {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
width: var(--window-width);
|
||||||
height: var(--window-height);
|
height: var(--window-height);
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
width: 100vw;
|
|
||||||
|
|
||||||
@include light-theme {
|
@include light-theme {
|
||||||
background: $color-gray-02;
|
background: $color-gray-02;
|
||||||
|
|
|
@ -4167,12 +4167,12 @@ button.module-image__border-overlay:focus {
|
||||||
|
|
||||||
&__overlay {
|
&__overlay {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
width: var(--window-width);
|
||||||
height: var(--window-height);
|
height: var(--window-height);
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
left: 0;
|
left: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 100vw;
|
|
||||||
z-index: $z-index-popup;
|
z-index: $z-index-popup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5857,7 +5857,7 @@ button.module-image__border-overlay:focus {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 100vw;
|
width: var(--window-width);
|
||||||
height: var(--window-height);
|
height: var(--window-height);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -7457,25 +7457,25 @@ button.module-image__border-overlay:focus {
|
||||||
|
|
||||||
.module-modal-host__overlay {
|
.module-modal-host__overlay {
|
||||||
background: $color-black-alpha-40;
|
background: $color-black-alpha-40;
|
||||||
|
width: var(--window-width);
|
||||||
height: var(--window-height);
|
height: var(--window-height);
|
||||||
left: 0;
|
left: var(--window-border);
|
||||||
position: absolute;
|
top: var(--titlebar-height);
|
||||||
top: 0;
|
position: fixed;
|
||||||
width: 100vw;
|
|
||||||
z-index: $z-index-popup-overlay;
|
z-index: $z-index-popup-overlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
.module-modal-host__overlay-container {
|
.module-modal-host__overlay-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
width: var(--window-width);
|
||||||
height: var(--window-height);
|
height: var(--window-height);
|
||||||
|
left: var(--window-border);
|
||||||
|
top: var(--titlebar-height);
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
left: 0;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
position: absolute;
|
position: fixed;
|
||||||
top: 0;
|
|
||||||
width: 100vw;
|
|
||||||
z-index: $z-index-popup-overlay;
|
z-index: $z-index-popup-overlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7612,9 +7612,9 @@ button.module-image__border-overlay:focus {
|
||||||
.module-progress-dialog__overlay {
|
.module-progress-dialog__overlay {
|
||||||
background: $color-black-alpha-40;
|
background: $color-black-alpha-40;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
left: var(--window-border);
|
||||||
top: 0;
|
top: var(--titlebar-height);
|
||||||
width: 100vw;
|
width: var(--window-width);
|
||||||
height: var(--window-height);
|
height: var(--window-height);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
|
@ -17,10 +17,20 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
--window-height: 100vh;
|
--window-height: 100vh;
|
||||||
|
--window-width: 100vw;
|
||||||
|
--unscaled-window-border: 0px;
|
||||||
|
--window-border: calc(var(--unscaled-window-border) / var(--zoom-factor));
|
||||||
--titlebar-height: 0px;
|
--titlebar-height: 0px;
|
||||||
|
|
||||||
&.os-windows:not(.full-screen) {
|
&.os-has-custom-titlebar:not(.full-screen) {
|
||||||
|
&:not(.maximized) {
|
||||||
|
--unscaled-window-border: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
--titlebar-height: calc(28px / var(--zoom-factor));
|
--titlebar-height: calc(28px / var(--zoom-factor));
|
||||||
--window-height: calc(100vh - var(--titlebar-height));
|
--window-width: calc(100vw - 2 * var(--window-border));
|
||||||
|
--window-height: calc(
|
||||||
|
100vh - var(--titlebar-height) - 2 * var(--window-border)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,12 @@
|
||||||
background: $color-gray-95;
|
background: $color-gray-95;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
width: var(--window-width);
|
||||||
height: var(--window-height);
|
height: var(--window-height);
|
||||||
left: 0;
|
left: 0;
|
||||||
position: absolute;
|
|
||||||
top: var(--titlebar-height);
|
top: var(--titlebar-height);
|
||||||
|
position: absolute;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
width: 100vw;
|
|
||||||
z-index: $z-index-popup-overlay;
|
z-index: $z-index-popup-overlay;
|
||||||
|
|
||||||
&__container {
|
&__container {
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 380px;
|
width: 380px;
|
||||||
padding-top: 42px;
|
padding-top: calc(14px + var(--title-bar-drag-area-height));
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -11,12 +11,12 @@
|
||||||
background: $color-gray-95;
|
background: $color-gray-95;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
width: var(--window-width);
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
left: 0;
|
left: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
width: 100vw;
|
|
||||||
z-index: $z-index-popup-overlay;
|
z-index: $z-index-popup-overlay;
|
||||||
|
|
||||||
&__container {
|
&__container {
|
||||||
|
|
|
@ -6,14 +6,41 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|
||||||
&__title {
|
--border-color: transparent;
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
--border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
border: var(--window-border) solid var(--border-color);
|
||||||
|
|
||||||
|
@mixin titlebar-position {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|
||||||
width: calc(100vw * var(--zoom-factor));
|
width: calc(100vw * var(--zoom-factor));
|
||||||
z-index: $z-index-window-controls;
|
z-index: $z-index-window-controls;
|
||||||
transform: scale(calc(1 / var(--zoom-factor)));
|
transform: scale(calc(1 / var(--zoom-factor)));
|
||||||
transform-origin: 0 0;
|
transform-origin: 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw bottom-less border frame around titlebar to prevent border-bottom
|
||||||
|
// color from leaking to corners.
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
|
||||||
|
@include titlebar-position;
|
||||||
|
|
||||||
|
height: calc(var(--titlebar-height) * var(--zoom-factor));
|
||||||
|
|
||||||
|
border: var(--unscaled-window-border) solid var(--border-color);
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
@include titlebar-position;
|
||||||
|
border: var(--unscaled-window-border) solid transparent;
|
||||||
|
|
||||||
// This matches the inline styles of frameless-titlebar
|
// This matches the inline styles of frameless-titlebar
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
||||||
|
@ -23,16 +50,13 @@
|
||||||
& button {
|
& button {
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Shift titlebar down 1px on Windows 11 because otherwise window border
|
&__padding {
|
||||||
// will cover it.
|
height: calc(var(--titlebar-height) - var(--window-border));
|
||||||
&--extra-padding {
|
|
||||||
padding-top: 1px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
margin-top: var(--titlebar-height);
|
|
||||||
height: var(--window-height);
|
height: var(--window-height);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
8
ts/OS.ts
8
ts/OS.ts
|
@ -16,7 +16,7 @@ export const isWindows = (minVersion?: string): boolean => {
|
||||||
|
|
||||||
return is.undefined(minVersion) ? true : semver.gte(osRelease, minVersion);
|
return is.undefined(minVersion) ? true : semver.gte(osRelease, minVersion);
|
||||||
};
|
};
|
||||||
export const isWindows11 = (): boolean => {
|
|
||||||
// See https://docs.microsoft.com/en-us/answers/questions/586619/windows-11-build-ver-is-still-10022000194.html
|
// Windows 10 and above
|
||||||
return isWindows('10.0.22000');
|
export const hasCustomTitleBar = (): boolean =>
|
||||||
};
|
isWindows('10.0.0') || Boolean(process.env.CUSTOM_TITLEBAR);
|
||||||
|
|
|
@ -1835,7 +1835,9 @@ export async function startApp(): Promise<void> {
|
||||||
window.reduxActions.app.openInstaller();
|
window.reduxActions.app.openInstaller();
|
||||||
}
|
}
|
||||||
|
|
||||||
window.registerForActive(() => notificationService.clear());
|
const { activeWindowService } = window.SignalContext;
|
||||||
|
|
||||||
|
activeWindowService.registerForActive(() => notificationService.clear());
|
||||||
window.addEventListener('unload', () => notificationService.fastClear());
|
window.addEventListener('unload', () => notificationService.fastClear());
|
||||||
|
|
||||||
notificationService.on('click', (id, messageId) => {
|
notificationService.on('click', (id, messageId) => {
|
||||||
|
@ -1848,7 +1850,7 @@ export async function startApp(): Promise<void> {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Maybe refresh remote configuration when we become active
|
// Maybe refresh remote configuration when we become active
|
||||||
window.registerForActive(async () => {
|
activeWindowService.registerForActive(async () => {
|
||||||
strictAssert(server !== undefined, 'WebAPI not ready');
|
strictAssert(server !== undefined, 'WebAPI not ready');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -14,8 +14,7 @@ export type PropsType = {
|
||||||
environment: string;
|
environment: string;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
version: string;
|
version: string;
|
||||||
platform: string;
|
hasCustomTitleBar: boolean;
|
||||||
isWindows11: boolean;
|
|
||||||
executeMenuRole: ExecuteMenuRoleType;
|
executeMenuRole: ExecuteMenuRoleType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,8 +23,7 @@ export const About = ({
|
||||||
i18n,
|
i18n,
|
||||||
environment,
|
environment,
|
||||||
version,
|
version,
|
||||||
platform,
|
hasCustomTitleBar,
|
||||||
isWindows11,
|
|
||||||
executeMenuRole,
|
executeMenuRole,
|
||||||
}: PropsType): JSX.Element => {
|
}: PropsType): JSX.Element => {
|
||||||
useEscapeHandling(closeAbout);
|
useEscapeHandling(closeAbout);
|
||||||
|
@ -34,8 +32,7 @@ export const About = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TitleBarContainer
|
<TitleBarContainer
|
||||||
platform={platform}
|
hasCustomTitleBar={hasCustomTitleBar}
|
||||||
isWindows11={isWindows11}
|
|
||||||
theme={theme}
|
theme={theme}
|
||||||
executeMenuRole={executeMenuRole}
|
executeMenuRole={executeMenuRole}
|
||||||
>
|
>
|
||||||
|
|
|
@ -36,8 +36,7 @@ type PropsType = {
|
||||||
isMaximized: boolean;
|
isMaximized: boolean;
|
||||||
isFullScreen: boolean;
|
isFullScreen: boolean;
|
||||||
menuOptions: MenuOptionsType;
|
menuOptions: MenuOptionsType;
|
||||||
platform: string;
|
hasCustomTitleBar: boolean;
|
||||||
isWindows11: boolean;
|
|
||||||
hideMenuBar: boolean;
|
hideMenuBar: boolean;
|
||||||
|
|
||||||
executeMenuRole: ExecuteMenuRoleType;
|
executeMenuRole: ExecuteMenuRoleType;
|
||||||
|
@ -59,11 +58,10 @@ export const App = ({
|
||||||
isFullScreen,
|
isFullScreen,
|
||||||
isMaximized,
|
isMaximized,
|
||||||
isShowingStoriesView,
|
isShowingStoriesView,
|
||||||
isWindows11,
|
hasCustomTitleBar,
|
||||||
localeMessages,
|
localeMessages,
|
||||||
menuOptions,
|
menuOptions,
|
||||||
openInbox,
|
openInbox,
|
||||||
platform,
|
|
||||||
registerSingleDevice,
|
registerSingleDevice,
|
||||||
renderCallManager,
|
renderCallManager,
|
||||||
renderCustomizingPreferredReactionsModal,
|
renderCustomizingPreferredReactionsModal,
|
||||||
|
@ -152,8 +150,7 @@ export const App = ({
|
||||||
theme={theme}
|
theme={theme}
|
||||||
isMaximized={isMaximized}
|
isMaximized={isMaximized}
|
||||||
isFullScreen={isFullScreen}
|
isFullScreen={isFullScreen}
|
||||||
platform={platform}
|
hasCustomTitleBar={hasCustomTitleBar}
|
||||||
isWindows11={isWindows11}
|
|
||||||
executeMenuRole={executeMenuRole}
|
executeMenuRole={executeMenuRole}
|
||||||
titleBarDoubleClick={titleBarDoubleClick}
|
titleBarDoubleClick={titleBarDoubleClick}
|
||||||
hasMenu
|
hasMenu
|
||||||
|
|
|
@ -26,8 +26,7 @@ const createProps = (): PropsType => ({
|
||||||
return 'https://picsum.photos/1800/900';
|
return 'https://picsum.photos/1800/900';
|
||||||
},
|
},
|
||||||
executeMenuRole: action('executeMenuRole'),
|
executeMenuRole: action('executeMenuRole'),
|
||||||
platform: 'win32',
|
hasCustomTitleBar: true,
|
||||||
isWindows11: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -31,8 +31,7 @@ export type PropsType = {
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
fetchLogs: () => Promise<string>;
|
fetchLogs: () => Promise<string>;
|
||||||
uploadLogs: (logs: string) => Promise<string>;
|
uploadLogs: (logs: string) => Promise<string>;
|
||||||
platform: string;
|
hasCustomTitleBar: boolean;
|
||||||
isWindows11: boolean;
|
|
||||||
executeMenuRole: ExecuteMenuRoleType;
|
executeMenuRole: ExecuteMenuRoleType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,8 +47,7 @@ export const DebugLogWindow = ({
|
||||||
i18n,
|
i18n,
|
||||||
fetchLogs,
|
fetchLogs,
|
||||||
uploadLogs,
|
uploadLogs,
|
||||||
platform,
|
hasCustomTitleBar,
|
||||||
isWindows11,
|
|
||||||
executeMenuRole,
|
executeMenuRole,
|
||||||
}: PropsType): JSX.Element => {
|
}: PropsType): JSX.Element => {
|
||||||
const [loadState, setLoadState] = useState<LoadState>(LoadState.NotStarted);
|
const [loadState, setLoadState] = useState<LoadState>(LoadState.NotStarted);
|
||||||
|
@ -147,8 +145,7 @@ export const DebugLogWindow = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TitleBarContainer
|
<TitleBarContainer
|
||||||
platform={platform}
|
hasCustomTitleBar={hasCustomTitleBar}
|
||||||
isWindows11={isWindows11}
|
|
||||||
theme={theme}
|
theme={theme}
|
||||||
executeMenuRole={executeMenuRole}
|
executeMenuRole={executeMenuRole}
|
||||||
>
|
>
|
||||||
|
@ -191,8 +188,7 @@ export const DebugLogWindow = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TitleBarContainer
|
<TitleBarContainer
|
||||||
platform={platform}
|
hasCustomTitleBar={hasCustomTitleBar}
|
||||||
isWindows11={isWindows11}
|
|
||||||
theme={theme}
|
theme={theme}
|
||||||
executeMenuRole={executeMenuRole}
|
executeMenuRole={executeMenuRole}
|
||||||
>
|
>
|
||||||
|
|
|
@ -158,8 +158,7 @@ const createProps = (): PropsType => ({
|
||||||
i18n,
|
i18n,
|
||||||
|
|
||||||
executeMenuRole: action('executeMenuRole'),
|
executeMenuRole: action('executeMenuRole'),
|
||||||
platform: 'win32',
|
hasCustomTitleBar: true,
|
||||||
isWindows11: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -102,8 +102,7 @@ export type PropsType = {
|
||||||
value: CustomColorType;
|
value: CustomColorType;
|
||||||
}
|
}
|
||||||
) => unknown;
|
) => unknown;
|
||||||
platform: string;
|
hasCustomTitleBar: boolean;
|
||||||
isWindows11: boolean;
|
|
||||||
executeMenuRole: ExecuteMenuRoleType;
|
executeMenuRole: ExecuteMenuRoleType;
|
||||||
|
|
||||||
// Limited support features
|
// Limited support features
|
||||||
|
@ -230,7 +229,7 @@ export const Preferences = ({
|
||||||
isNotificationAttentionSupported,
|
isNotificationAttentionSupported,
|
||||||
isSyncSupported,
|
isSyncSupported,
|
||||||
isSystemTraySupported,
|
isSystemTraySupported,
|
||||||
isWindows11,
|
hasCustomTitleBar,
|
||||||
lastSyncTime,
|
lastSyncTime,
|
||||||
makeSyncRequest,
|
makeSyncRequest,
|
||||||
notificationContent,
|
notificationContent,
|
||||||
|
@ -258,7 +257,6 @@ export const Preferences = ({
|
||||||
onThemeChange,
|
onThemeChange,
|
||||||
onUniversalExpireTimerChange,
|
onUniversalExpireTimerChange,
|
||||||
onZoomFactorChange,
|
onZoomFactorChange,
|
||||||
platform,
|
|
||||||
removeCustomColor,
|
removeCustomColor,
|
||||||
removeCustomColorOnConversations,
|
removeCustomColorOnConversations,
|
||||||
resetAllChatColors,
|
resetAllChatColors,
|
||||||
|
@ -1028,8 +1026,7 @@ export const Preferences = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TitleBarContainer
|
<TitleBarContainer
|
||||||
platform={platform}
|
hasCustomTitleBar={hasCustomTitleBar}
|
||||||
isWindows11={isWindows11}
|
|
||||||
theme={theme}
|
theme={theme}
|
||||||
executeMenuRole={executeMenuRole}
|
executeMenuRole={executeMenuRole}
|
||||||
>
|
>
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { createTemplate } from '../../app/menu';
|
||||||
import { ThemeType } from '../types/Util';
|
import { ThemeType } from '../types/Util';
|
||||||
import type { LocaleMessagesType } from '../types/I18N';
|
import type { LocaleMessagesType } from '../types/I18N';
|
||||||
import type { MenuOptionsType, MenuActionType } from '../types/menu';
|
import type { MenuOptionsType, MenuActionType } from '../types/menu';
|
||||||
|
import { useIsWindowActive } from '../hooks/useIsWindowActive';
|
||||||
|
|
||||||
export type MenuPropsType = Readonly<{
|
export type MenuPropsType = Readonly<{
|
||||||
hasMenu: true;
|
hasMenu: true;
|
||||||
|
@ -28,9 +29,8 @@ export type PropsType = Readonly<{
|
||||||
theme: ThemeType;
|
theme: ThemeType;
|
||||||
isMaximized?: boolean;
|
isMaximized?: boolean;
|
||||||
isFullScreen?: boolean;
|
isFullScreen?: boolean;
|
||||||
isWindows11: boolean;
|
hasCustomTitleBar: boolean;
|
||||||
hideMenuBar?: boolean;
|
hideMenuBar?: boolean;
|
||||||
platform: string;
|
|
||||||
executeMenuRole: ExecuteMenuRoleType;
|
executeMenuRole: ExecuteMenuRoleType;
|
||||||
titleBarDoubleClick?: () => void;
|
titleBarDoubleClick?: () => void;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
@ -116,16 +116,17 @@ export const TitleBarContainer = (props: PropsType): JSX.Element => {
|
||||||
theme,
|
theme,
|
||||||
isMaximized,
|
isMaximized,
|
||||||
isFullScreen,
|
isFullScreen,
|
||||||
isWindows11,
|
hasCustomTitleBar,
|
||||||
hideMenuBar,
|
hideMenuBar,
|
||||||
executeMenuRole,
|
executeMenuRole,
|
||||||
titleBarDoubleClick,
|
titleBarDoubleClick,
|
||||||
children,
|
children,
|
||||||
hasMenu,
|
hasMenu,
|
||||||
platform,
|
|
||||||
iconSrc = 'images/icon_32.png',
|
iconSrc = 'images/icon_32.png',
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
const isWindowActive = useIsWindowActive();
|
||||||
|
|
||||||
const titleBarTheme = useMemo(
|
const titleBarTheme = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
bar: {
|
bar: {
|
||||||
|
@ -201,7 +202,7 @@ export const TitleBarContainer = (props: PropsType): JSX.Element => {
|
||||||
[theme, hideMenuBar]
|
[theme, hideMenuBar]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (platform !== 'win32' || isFullScreen) {
|
if (!hasCustomTitleBar || isFullScreen) {
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,17 +237,18 @@ export const TitleBarContainer = (props: PropsType): JSX.Element => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="TitleBarContainer">
|
<div
|
||||||
<TitleBar
|
className={classNames(
|
||||||
className={classNames(
|
'TitleBarContainer',
|
||||||
'TitleBarContainer__title',
|
isWindowActive ? 'TitleBarContainer--active' : null
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="TitleBarContainer__padding" />
|
||||||
|
<div className="TitleBarContainer__content">{children}</div>
|
||||||
|
|
||||||
// Add a pixel of padding on non-maximized Windows 11 titlebar.
|
<TitleBar
|
||||||
isWindows11 && !isMaximized
|
className="TitleBarContainer__title"
|
||||||
? 'TitleBarContainer__title--extra-padding'
|
platform="win32"
|
||||||
: null
|
|
||||||
)}
|
|
||||||
platform={platform}
|
|
||||||
iconSrc={iconSrc}
|
iconSrc={iconSrc}
|
||||||
theme={titleBarTheme}
|
theme={titleBarTheme}
|
||||||
maximized={isMaximized}
|
maximized={isMaximized}
|
||||||
|
@ -254,8 +256,6 @@ export const TitleBarContainer = (props: PropsType): JSX.Element => {
|
||||||
onDoubleClick={titleBarDoubleClick}
|
onDoubleClick={titleBarDoubleClick}
|
||||||
hideControls
|
hideControls
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="TitleBarContainer__content">{children}</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,11 +36,6 @@ export default {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const noop = () => {};
|
const noop = () => {};
|
||||||
|
|
||||||
Object.assign(window, {
|
|
||||||
registerForActive: noop,
|
|
||||||
unregisterForActive: noop,
|
|
||||||
});
|
|
||||||
|
|
||||||
const items: Record<string, TimelineItemType> = {
|
const items: Record<string, TimelineItemType> = {
|
||||||
'id-1': {
|
'id-1': {
|
||||||
type: 'message',
|
type: 'message',
|
||||||
|
|
|
@ -573,7 +573,9 @@ export class Timeline extends React.Component<
|
||||||
|
|
||||||
this.updateIntersectionObserver();
|
this.updateIntersectionObserver();
|
||||||
|
|
||||||
window.registerForActive(this.markNewestBottomVisibleMessageRead);
|
window.SignalContext.activeWindowService.registerForActive(
|
||||||
|
this.markNewestBottomVisibleMessageRead
|
||||||
|
);
|
||||||
|
|
||||||
this.delayedPeekTimeout = setTimeout(() => {
|
this.delayedPeekTimeout = setTimeout(() => {
|
||||||
const { id, peekGroupCallForTheFirstTime } = this.props;
|
const { id, peekGroupCallForTheFirstTime } = this.props;
|
||||||
|
@ -590,7 +592,9 @@ export class Timeline extends React.Component<
|
||||||
public override componentWillUnmount(): void {
|
public override componentWillUnmount(): void {
|
||||||
const { delayedPeekTimeout, peekInterval } = this;
|
const { delayedPeekTimeout, peekInterval } = this;
|
||||||
|
|
||||||
window.unregisterForActive(this.markNewestBottomVisibleMessageRead);
|
window.SignalContext.activeWindowService.unregisterForActive(
|
||||||
|
this.markNewestBottomVisibleMessageRead
|
||||||
|
);
|
||||||
|
|
||||||
this.intersectionObserver?.disconnect();
|
this.intersectionObserver?.disconnect();
|
||||||
|
|
||||||
|
|
23
ts/hooks/useIsWindowActive.ts
Normal file
23
ts/hooks/useIsWindowActive.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright 2022 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export function useIsWindowActive(): boolean {
|
||||||
|
const { activeWindowService } = window.SignalContext;
|
||||||
|
const [isActive, setIsActive] = useState(activeWindowService.isActive());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const update = (newIsActive: boolean): void => {
|
||||||
|
setIsActive(newIsActive);
|
||||||
|
};
|
||||||
|
|
||||||
|
activeWindowService.registerForChange(update);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
activeWindowService.unregisterForChange(update);
|
||||||
|
};
|
||||||
|
}, [activeWindowService]);
|
||||||
|
|
||||||
|
return isActive;
|
||||||
|
}
|
|
@ -1,10 +1,14 @@
|
||||||
// Copyright 2021 Signal Messenger, LLC
|
// Copyright 2021-2022 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
{
|
{
|
||||||
const updateFullScreenClass = (isFullScreen: boolean) => {
|
const updateFullScreenClass = (
|
||||||
|
isFullScreen: boolean,
|
||||||
|
isMaximized: boolean
|
||||||
|
) => {
|
||||||
document.body.classList.toggle('full-screen', isFullScreen);
|
document.body.classList.toggle('full-screen', isFullScreen);
|
||||||
|
document.body.classList.toggle('maximized', isMaximized);
|
||||||
};
|
};
|
||||||
updateFullScreenClass(window.isFullScreen());
|
updateFullScreenClass(window.isFullScreen(), window.isMaximized());
|
||||||
window.onFullScreenChange = updateFullScreenClass;
|
window.onFullScreenChange = updateFullScreenClass;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1417,7 +1417,7 @@ export class ConversationModel extends window.Backbone
|
||||||
messagesAdded({
|
messagesAdded({
|
||||||
conversationId,
|
conversationId,
|
||||||
messages: [{ ...message.attributes }],
|
messages: [{ ...message.attributes }],
|
||||||
isActive: window.isActive(),
|
isActive: window.SignalContext.activeWindowService.isActive(),
|
||||||
isJustSent,
|
isJustSent,
|
||||||
isNewMessage: true,
|
isNewMessage: true,
|
||||||
});
|
});
|
||||||
|
@ -1567,7 +1567,7 @@ export class ConversationModel extends window.Backbone
|
||||||
messages: cleaned.map((messageModel: MessageModel) => ({
|
messages: cleaned.map((messageModel: MessageModel) => ({
|
||||||
...messageModel.attributes,
|
...messageModel.attributes,
|
||||||
})),
|
})),
|
||||||
isActive: window.isActive(),
|
isActive: window.SignalContext.activeWindowService.isActive(),
|
||||||
isJustSent: false,
|
isJustSent: false,
|
||||||
isNewMessage: false,
|
isNewMessage: false,
|
||||||
});
|
});
|
||||||
|
@ -1620,7 +1620,7 @@ export class ConversationModel extends window.Backbone
|
||||||
messages: cleaned.map((messageModel: MessageModel) => ({
|
messages: cleaned.map((messageModel: MessageModel) => ({
|
||||||
...messageModel.attributes,
|
...messageModel.attributes,
|
||||||
})),
|
})),
|
||||||
isActive: window.isActive(),
|
isActive: window.SignalContext.activeWindowService.isActive(),
|
||||||
isJustSent: false,
|
isJustSent: false,
|
||||||
isNewMessage: false,
|
isNewMessage: false,
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,6 +25,8 @@ export class ActiveWindowService {
|
||||||
|
|
||||||
private activeCallbacks: Array<() => void> = [];
|
private activeCallbacks: Array<() => void> = [];
|
||||||
|
|
||||||
|
private changeCallbacks: Array<(isActive: boolean) => void> = [];
|
||||||
|
|
||||||
private lastActiveEventAt = -Infinity;
|
private lastActiveEventAt = -Infinity;
|
||||||
|
|
||||||
private callActiveCallbacks: () => void;
|
private callActiveCallbacks: () => void;
|
||||||
|
@ -73,6 +75,16 @@ export class ActiveWindowService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerForChange(callback: (isActive: boolean) => void): void {
|
||||||
|
this.changeCallbacks.push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
unregisterForChange(callback: (isActive: boolean) => void): void {
|
||||||
|
this.changeCallbacks = this.changeCallbacks.filter(
|
||||||
|
item => item !== callback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private onActiveEvent(): void {
|
private onActiveEvent(): void {
|
||||||
this.updateState(() => {
|
this.updateState(() => {
|
||||||
this.lastActiveEventAt = Date.now();
|
this.lastActiveEventAt = Date.now();
|
||||||
|
@ -93,5 +105,11 @@ export class ActiveWindowService {
|
||||||
if (!wasActiveBefore && isActiveNow) {
|
if (!wasActiveBefore && isActiveNow) {
|
||||||
this.callActiveCallbacks();
|
this.callActiveCallbacks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wasActiveBefore !== isActiveNow) {
|
||||||
|
for (const callback of this.changeCallbacks) {
|
||||||
|
callback(isActiveNow);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,7 +228,7 @@ class NotificationService extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { notificationData } = this;
|
const { notificationData } = this;
|
||||||
const isAppFocused = window.isActive();
|
const isAppFocused = window.SignalContext.activeWindowService.isActive();
|
||||||
const userSetting = this.getNotificationSetting();
|
const userSetting = this.getNotificationSetting();
|
||||||
|
|
||||||
// This isn't a boolean because TypeScript isn't smart enough to know that, if
|
// This isn't a boolean because TypeScript isn't smart enough to know that, if
|
||||||
|
|
|
@ -14,4 +14,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.classList.add(className);
|
document.body.classList.add(className);
|
||||||
|
|
||||||
|
if (window.SignalContext.OS.hasCustomTitleBar()) {
|
||||||
|
document.body.classList.add('os-has-custom-titlebar');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import {
|
||||||
getIsMainWindowMaximized,
|
getIsMainWindowMaximized,
|
||||||
getIsMainWindowFullScreen,
|
getIsMainWindowFullScreen,
|
||||||
getMenuOptions,
|
getMenuOptions,
|
||||||
getPlatform,
|
|
||||||
} from '../selectors/user';
|
} from '../selectors/user';
|
||||||
import { shouldShowStoriesView } from '../selectors/stories';
|
import { shouldShowStoriesView } from '../selectors/stories';
|
||||||
import { getHideMenuBar } from '../selectors/items';
|
import { getHideMenuBar } from '../selectors/items';
|
||||||
|
@ -42,8 +41,7 @@ const mapStateToProps = (state: StateType) => {
|
||||||
isMaximized: getIsMainWindowMaximized(state),
|
isMaximized: getIsMainWindowMaximized(state),
|
||||||
isFullScreen: getIsMainWindowFullScreen(state),
|
isFullScreen: getIsMainWindowFullScreen(state),
|
||||||
menuOptions: getMenuOptions(state),
|
menuOptions: getMenuOptions(state),
|
||||||
platform: getPlatform(state),
|
hasCustomTitleBar: window.SignalContext.OS.hasCustomTitleBar(),
|
||||||
isWindows11: window.SignalContext.OS.isWindows11(),
|
|
||||||
hideMenuBar: getHideMenuBar(state),
|
hideMenuBar: getHideMenuBar(state),
|
||||||
renderCallManager: () => <SmartCallManager />,
|
renderCallManager: () => <SmartCallManager />,
|
||||||
renderCustomizingPreferredReactionsModal: () => (
|
renderCustomizingPreferredReactionsModal: () => (
|
||||||
|
|
|
@ -53,7 +53,8 @@ async function notifyForCall(
|
||||||
isVideoCall: boolean
|
isVideoCall: boolean
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const shouldNotify =
|
const shouldNotify =
|
||||||
!window.isActive() && window.Events.getCallSystemNotification();
|
!window.SignalContext.activeWindowService.isActive() &&
|
||||||
|
window.Events.getCallSystemNotification();
|
||||||
if (!shouldNotify) {
|
if (!shouldNotify) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,7 @@ export const rendererConfigSchema = z.object({
|
||||||
|
|
||||||
// Only used by main window
|
// Only used by main window
|
||||||
isMainWindowFullScreen: z.boolean(),
|
isMainWindowFullScreen: z.boolean(),
|
||||||
|
isMainWindowMaximized: z.boolean(),
|
||||||
|
|
||||||
// Only for tests
|
// Only for tests
|
||||||
argv: configOptionalStringSchema,
|
argv: configOptionalStringSchema,
|
||||||
|
|
|
@ -450,7 +450,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
||||||
};
|
};
|
||||||
|
|
||||||
const markMessageRead = async (messageId: string) => {
|
const markMessageRead = async (messageId: string) => {
|
||||||
if (!window.isActive()) {
|
if (!window.SignalContext.activeWindowService.isActive()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
6
ts/window.d.ts
vendored
6
ts/window.d.ts
vendored
|
@ -291,10 +291,10 @@ declare global {
|
||||||
waitForEmptyEventQueue: () => Promise<void>;
|
waitForEmptyEventQueue: () => Promise<void>;
|
||||||
getVersion: () => string;
|
getVersion: () => string;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
isActive: () => boolean;
|
|
||||||
isAfterVersion: (version: string, anotherVersion: string) => boolean;
|
isAfterVersion: (version: string, anotherVersion: string) => boolean;
|
||||||
isBeforeVersion: (version: string, anotherVersion: string) => boolean;
|
isBeforeVersion: (version: string, anotherVersion: string) => boolean;
|
||||||
isFullScreen: () => boolean;
|
isFullScreen: () => boolean;
|
||||||
|
isMaximized: () => boolean;
|
||||||
initialTheme?: ThemeType;
|
initialTheme?: ThemeType;
|
||||||
libphonenumberInstance: {
|
libphonenumberInstance: {
|
||||||
parse: (number: string) => PhoneNumber;
|
parse: (number: string) => PhoneNumber;
|
||||||
|
@ -303,12 +303,11 @@ declare global {
|
||||||
};
|
};
|
||||||
libphonenumberFormat: typeof PhoneNumberFormat;
|
libphonenumberFormat: typeof PhoneNumberFormat;
|
||||||
nodeSetImmediate: typeof setImmediate;
|
nodeSetImmediate: typeof setImmediate;
|
||||||
onFullScreenChange: (fullScreen: boolean) => void;
|
onFullScreenChange: (fullScreen: boolean, maximized: boolean) => void;
|
||||||
platform: string;
|
platform: string;
|
||||||
preloadedImages: Array<WhatIsThis>;
|
preloadedImages: Array<WhatIsThis>;
|
||||||
reduxActions: ReduxActions;
|
reduxActions: ReduxActions;
|
||||||
reduxStore: Store<StateType>;
|
reduxStore: Store<StateType>;
|
||||||
registerForActive: (handler: () => void) => void;
|
|
||||||
restart: () => void;
|
restart: () => void;
|
||||||
setImmediate: typeof setImmediate;
|
setImmediate: typeof setImmediate;
|
||||||
showWindow: () => void;
|
showWindow: () => void;
|
||||||
|
@ -326,7 +325,6 @@ declare global {
|
||||||
systemTheme: WhatIsThis;
|
systemTheme: WhatIsThis;
|
||||||
textsecure: typeof textsecure;
|
textsecure: typeof textsecure;
|
||||||
titleBarDoubleClick: () => void;
|
titleBarDoubleClick: () => void;
|
||||||
unregisterForActive: (handler: () => void) => void;
|
|
||||||
updateTrayIcon: (count: number) => void;
|
updateTrayIcon: (count: number) => void;
|
||||||
Backbone: typeof Backbone;
|
Backbone: typeof Backbone;
|
||||||
CI?: CI;
|
CI?: CI;
|
||||||
|
|
|
@ -36,8 +36,7 @@ contextBridge.exposeInMainWorld('SignalContext', {
|
||||||
environment: `${environmentText.join(' - ')}${platform}`,
|
environment: `${environmentText.join(' - ')}${platform}`,
|
||||||
i18n: SignalContext.i18n,
|
i18n: SignalContext.i18n,
|
||||||
version: SignalContext.getVersion(),
|
version: SignalContext.getVersion(),
|
||||||
platform: process.platform,
|
hasCustomTitleBar: SignalContext.OS.hasCustomTitleBar(),
|
||||||
isWindows11: SignalContext.OS.isWindows11(),
|
|
||||||
executeMenuRole: SignalContext.executeMenuRole,
|
executeMenuRole: SignalContext.executeMenuRole,
|
||||||
}),
|
}),
|
||||||
document.getElementById('app')
|
document.getElementById('app')
|
||||||
|
|
|
@ -12,6 +12,7 @@ import type { LocaleMessagesType } from '../types/I18N';
|
||||||
import type { NativeThemeType } from '../context/createNativeThemeListener';
|
import type { NativeThemeType } from '../context/createNativeThemeListener';
|
||||||
import type { SettingType } from '../util/preload';
|
import type { SettingType } from '../util/preload';
|
||||||
import type { RendererConfigType } from '../types/RendererConfig';
|
import type { RendererConfigType } from '../types/RendererConfig';
|
||||||
|
import { ActiveWindowService } from '../services/ActiveWindowService';
|
||||||
|
|
||||||
import { Bytes } from '../context/Bytes';
|
import { Bytes } from '../context/Bytes';
|
||||||
import { Crypto } from '../context/Crypto';
|
import { Crypto } from '../context/Crypto';
|
||||||
|
@ -28,7 +29,10 @@ import { createSetting } from '../util/preload';
|
||||||
import { initialize as initializeLogging } from '../logging/set_up_renderer_logging';
|
import { initialize as initializeLogging } from '../logging/set_up_renderer_logging';
|
||||||
import { waitForSettingsChange } from './waitForSettingsChange';
|
import { waitForSettingsChange } from './waitForSettingsChange';
|
||||||
import { createNativeThemeListener } from '../context/createNativeThemeListener';
|
import { createNativeThemeListener } from '../context/createNativeThemeListener';
|
||||||
import { isWindows, isWindows11, isLinux, isMacOS } from '../OS';
|
import { isWindows, isLinux, isMacOS, hasCustomTitleBar } from '../OS';
|
||||||
|
|
||||||
|
const activeWindowService = new ActiveWindowService();
|
||||||
|
activeWindowService.initialize(window.document, ipcRenderer);
|
||||||
|
|
||||||
const params = new URLSearchParams(document.location.search);
|
const params = new URLSearchParams(document.location.search);
|
||||||
const configParam = params.get('config');
|
const configParam = params.get('config');
|
||||||
|
@ -58,6 +62,7 @@ export type SignalContextType = {
|
||||||
nativeThemeListener: NativeThemeType;
|
nativeThemeListener: NativeThemeType;
|
||||||
setIsCallActive: (isCallActive: boolean) => unknown;
|
setIsCallActive: (isCallActive: boolean) => unknown;
|
||||||
|
|
||||||
|
activeWindowService: typeof activeWindowService;
|
||||||
Settings: {
|
Settings: {
|
||||||
themeSetting: SettingType<IPCEventsValuesType['themeSetting']>;
|
themeSetting: SettingType<IPCEventsValuesType['themeSetting']>;
|
||||||
waitForChange: () => Promise<void>;
|
waitForChange: () => Promise<void>;
|
||||||
|
@ -65,9 +70,9 @@ export type SignalContextType = {
|
||||||
OS: {
|
OS: {
|
||||||
platform: string;
|
platform: string;
|
||||||
isWindows: typeof isWindows;
|
isWindows: typeof isWindows;
|
||||||
isWindows11: typeof isWindows11;
|
|
||||||
isLinux: typeof isLinux;
|
isLinux: typeof isLinux;
|
||||||
isMacOS: typeof isMacOS;
|
isMacOS: typeof isMacOS;
|
||||||
|
hasCustomTitleBar: typeof hasCustomTitleBar;
|
||||||
};
|
};
|
||||||
config: RendererConfigType;
|
config: RendererConfigType;
|
||||||
getAppInstance: () => string | undefined;
|
getAppInstance: () => string | undefined;
|
||||||
|
@ -86,6 +91,7 @@ export type SignalContextType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SignalContext: SignalContextType = {
|
export const SignalContext: SignalContextType = {
|
||||||
|
activeWindowService,
|
||||||
Settings: {
|
Settings: {
|
||||||
themeSetting: createSetting('themeSetting', { setter: false }),
|
themeSetting: createSetting('themeSetting', { setter: false }),
|
||||||
waitForChange: waitForSettingsChange,
|
waitForChange: waitForSettingsChange,
|
||||||
|
@ -93,9 +99,9 @@ export const SignalContext: SignalContextType = {
|
||||||
OS: {
|
OS: {
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
isWindows,
|
isWindows,
|
||||||
isWindows11,
|
|
||||||
isLinux,
|
isLinux,
|
||||||
isMacOS,
|
isMacOS,
|
||||||
|
hasCustomTitleBar,
|
||||||
},
|
},
|
||||||
bytes: new Bytes(),
|
bytes: new Bytes(),
|
||||||
config,
|
config,
|
||||||
|
|
|
@ -26,8 +26,7 @@ contextBridge.exposeInMainWorld('SignalContext', {
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
React.createElement(DebugLogWindow, {
|
React.createElement(DebugLogWindow, {
|
||||||
platform: process.platform,
|
hasCustomTitleBar: SignalContext.OS.hasCustomTitleBar(),
|
||||||
isWindows11: SignalContext.OS.isWindows11(),
|
|
||||||
executeMenuRole: SignalContext.executeMenuRole,
|
executeMenuRole: SignalContext.executeMenuRole,
|
||||||
closeWindow: () => SignalContext.executeMenuRole('close'),
|
closeWindow: () => SignalContext.executeMenuRole('close'),
|
||||||
downloadLog: (logText: string) =>
|
downloadLog: (logText: string) =>
|
||||||
|
|
|
@ -254,14 +254,17 @@ window.sendChallengeRequest = request => ipc.send('challenge:request', request);
|
||||||
|
|
||||||
{
|
{
|
||||||
let isFullScreen = Boolean(config.isMainWindowFullScreen);
|
let isFullScreen = Boolean(config.isMainWindowFullScreen);
|
||||||
|
let isMaximized = Boolean(config.isMainWindowMaximized);
|
||||||
|
|
||||||
window.isFullScreen = () => isFullScreen;
|
window.isFullScreen = () => isFullScreen;
|
||||||
|
window.isMaximized = () => isMaximized;
|
||||||
// This is later overwritten.
|
// This is later overwritten.
|
||||||
window.onFullScreenChange = noop;
|
window.onFullScreenChange = noop;
|
||||||
|
|
||||||
ipc.on('full-screen-change', (_event, isFull) => {
|
ipc.on('window:set-window-stats', (_event, stats) => {
|
||||||
isFullScreen = Boolean(isFull);
|
isFullScreen = Boolean(stats.isFullScreen);
|
||||||
window.onFullScreenChange(isFullScreen);
|
isMaximized = Boolean(stats.isMaximized);
|
||||||
|
window.onFullScreenChange(isFullScreen, isMaximized);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright 2022 Signal Messenger, LLC
|
// Copyright 2022 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import { ipcRenderer as ipc } from 'electron';
|
|
||||||
import Backbone from 'backbone';
|
import Backbone from 'backbone';
|
||||||
import { PhoneNumberUtil, PhoneNumberFormat } from 'google-libphonenumber';
|
import { PhoneNumberUtil, PhoneNumberFormat } from 'google-libphonenumber';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
@ -12,7 +11,6 @@ import PQueue from 'p-queue';
|
||||||
|
|
||||||
import { textsecure } from '../../textsecure';
|
import { textsecure } from '../../textsecure';
|
||||||
import { imageToBlurHash } from '../../util/imageToBlurHash';
|
import { imageToBlurHash } from '../../util/imageToBlurHash';
|
||||||
import { ActiveWindowService } from '../../services/ActiveWindowService';
|
|
||||||
import * as Attachments from '../attachments';
|
import * as Attachments from '../attachments';
|
||||||
import { setup } from '../../signal';
|
import { setup } from '../../signal';
|
||||||
import { addSensitivePath } from '../../util/privacy';
|
import { addSensitivePath } from '../../util/privacy';
|
||||||
|
@ -44,14 +42,6 @@ window.imageToBlurHash = imageToBlurHash;
|
||||||
window.libphonenumberInstance = PhoneNumberUtil.getInstance();
|
window.libphonenumberInstance = PhoneNumberUtil.getInstance();
|
||||||
window.libphonenumberFormat = PhoneNumberFormat;
|
window.libphonenumberFormat = PhoneNumberFormat;
|
||||||
|
|
||||||
const activeWindowService = new ActiveWindowService();
|
|
||||||
activeWindowService.initialize(window.document, ipc);
|
|
||||||
window.isActive = activeWindowService.isActive.bind(activeWindowService);
|
|
||||||
window.registerForActive =
|
|
||||||
activeWindowService.registerForActive.bind(activeWindowService);
|
|
||||||
window.unregisterForActive =
|
|
||||||
activeWindowService.unregisterForActive.bind(activeWindowService);
|
|
||||||
|
|
||||||
window.React = React;
|
window.React = React;
|
||||||
window.ReactDOM = ReactDOM;
|
window.ReactDOM = ReactDOM;
|
||||||
window.PQueue = PQueue;
|
window.PQueue = PQueue;
|
||||||
|
|
|
@ -341,8 +341,7 @@ const renderPreferences = async () => {
|
||||||
|
|
||||||
i18n: SignalContext.i18n,
|
i18n: SignalContext.i18n,
|
||||||
|
|
||||||
platform: process.platform,
|
hasCustomTitleBar: SignalContext.OS.hasCustomTitleBar(),
|
||||||
isWindows11: SignalContext.OS.isWindows11(),
|
|
||||||
executeMenuRole: SignalContext.executeMenuRole,
|
executeMenuRole: SignalContext.executeMenuRole,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue