Repository navigation
feat: NethVoice chat #229
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
edospadoni
wants to merge
50
commits into
main
Choose a base branch
from
nethvoice-chat
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
feat: NethVoice chat #229
Changes from all commits
Commits
Show all changes
50 commits
Select commit
Hold shift + click to select a range
a32ad2f
feat: NethVoice chat
edospadoni f718402
chat: drag the chat window from the dock and the header
edospadoni 65e0dd7
chat: keep the chat window on screen while dragging; island 0.1.4
edospadoni bf819cb
chat: retry the chat check, resend its state when NethLink reloads
edospadoni a46ffec
command bar: chat with a colleague
edospadoni 2efbbb2
chat: richer desktop notification; island 0.1.5
edospadoni 2e1ba8b
search: chat with a colleague from the results
edospadoni 3bdd3c7
command bar: only Call up top, chat stays on colleague rows
edospadoni 1eb498c
chat: delete or leave from the list, translations, group names; islan…
edospadoni e05567b
chat: Messages list like the last calls, one chat icon, chat after la…
edospadoni a28cf6a
chat: island 0.1.8 (replies)
edospadoni 10d2f19
chat: shared contacts mapping and short dates; island 0.1.9
edospadoni 1457344
chat: operator instead of colleague; island 0.1.10
edospadoni 7074d1f
chat: operators no longer active labelled; island 0.1.11
edospadoni 615ecd1
chat: island 0.1.12
edospadoni 55ffbe8
chat: island 0.1.13 (chat.internal)
edospadoni 962e891
chat: island 0.1.14
edospadoni 2238357
chat: island 0.1.15; shadows end inside the window
edospadoni 1d48332
chat: island 0.1.16; the island's rings survive the shorter shadow
edospadoni 03ae1fa
chat: island 0.1.17 (delivered and read ticks)
edospadoni 0892b4a
chat: island 0.1.18; the expanded chat is not capped by the window
edospadoni 16a625f
chat: island 0.1.19
edospadoni 1c161b5
chat: island 0.1.20
edospadoni 646540d
chat: review fixes, island 0.1.23
edospadoni 960cd75
chat: island 0.1.26
edospadoni 8d26aad
chat: island 0.1.27
edospadoni 4d1173d
chat: island 0.1.28, native tooltips on the heads (the window is size…
edospadoni e03d426
chat: island 0.1.28 (with nativeTooltips); drop a tsc build file
edospadoni 06bba8e
chat: island 0.1.29
edospadoni 9b4d8c2
chat: island 0.1.30; the list shows a group's size and "No messages y…
edospadoni df7db75
chat: island 0.1.31; group looks as in the CTI
edospadoni cb6a183
chat: island 0.1.32
edospadoni cad0927
chat: island 0.1.33
edospadoni a3552aa
chat: message icon in the menu, as in the CTI
edospadoni 89b4e41
chat: island 0.1.34; the same message icons as the CTI
edospadoni 36c0ea5
chat: island 0.1.35
edospadoni fae4e23
chat: island 0.1.36
edospadoni 668b45f
chat: island 0.1.37; the window grows from the corner nearest the dock
edospadoni 92a9ce9
chat: island 0.1.38
edospadoni edb62e7
chat: island 0.1.39
edospadoni 5900d84
chat-island 0.1.40
edospadoni 51792d1
chat: blue accent, registered devices for the island, chat-island 0.1.41
edospadoni 36d99f3
chat-island 0.1.42
edospadoni 5fb0eab
chat-island 0.1.43
edospadoni 628c0db
chat: island in NethLink's language, chat-island 0.1.44
edospadoni 1705200
lockfile on main
edospadoni c713b41
chat: the dock's tooltips in the page, the window widens to show them…
edospadoni b57c231
chat: chat-island 0.1.45
edospadoni fe2bc92
chat: chat-island 0.1.46
edospadoni 32debaa
chat: chat-island 0.1.47
edospadoni File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| import { ChatIslandWindow } from '../windows' | ||
| import { Log } from '@shared/utils/logger' | ||
| import { IPC_EVENTS } from '@shared/constants' | ||
| import { screen } from 'electron' | ||
|
|
||
| type Corner = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | ||
| type Rect = { x: number; y: number; w: number; h: number } | ||
|
|
||
| // Between the window's edge and the dock: the island's right-4/left-4 and bottom-8/top-8. | ||
| const SIDE = 16 | ||
| const END = 32 | ||
|
|
||
| export class ChatIslandController { | ||
| static instance: ChatIslandController | undefined | ||
| window: ChatIslandWindow | ||
| private wantFocus = false | ||
| private focusTimer: ReturnType<typeof setTimeout> | undefined | ||
| // The work area's corner nearest the dock, and the window's distance from its two edges, kept after a drag. | ||
| private corner: Corner = 'bottom-right' | ||
| private anchor = { x: 0, y: 0 } | ||
| private displayId: number | undefined | ||
| private size: { w: number; h: number; dock?: Rect; corner?: Corner } = { | ||
| w: 0, | ||
| h: 0, | ||
| } | ||
|
|
||
| constructor() { | ||
| ChatIslandController.instance = this | ||
| this.window = new ChatIslandWindow() | ||
| } | ||
|
|
||
| // Sized to the island, grown from the corner nearest the dock; hidden when there is nothing to show. | ||
| resize(size: { w: number; h: number; dock?: Rect; corner?: Corner }) { | ||
| try { | ||
| this.size = size | ||
| // A reloaded island starts from the default corner: tell it the current one. | ||
| if (size.corner && size.corner !== this.corner) | ||
| this.window.emit( | ||
| IPC_EVENTS.CHAT_TO_ISLAND, | ||
| 'chat-island-corner', | ||
| this.corner, | ||
| ) | ||
| const window = this.window.getWindow() | ||
| if (!window) return | ||
| if (size.w < 8 || size.h < 8) { | ||
| window.hide() | ||
| return | ||
| } | ||
| const { x, y, width, height } = this.workArea() | ||
| const w = Math.min(Math.ceil(size.w), width) | ||
| const h = Math.min(Math.ceil(size.h), height) | ||
| const left = this.corner.endsWith('left') | ||
| ? x + this.anchor.x | ||
| : x + width - this.anchor.x - w | ||
| const top = this.corner.startsWith('top') | ||
| ? y + this.anchor.y | ||
| : y + height - this.anchor.y - h | ||
| window.setBounds({ | ||
| x: Math.min(Math.max(x, left), x + width - w), | ||
| y: Math.min(Math.max(y, top), y + height - h), | ||
| width: w, | ||
| height: h, | ||
| }) | ||
| if (this.wantFocus) { | ||
| this.wantFocus = false | ||
| window.show() | ||
| window.focus() | ||
| } else if (!window.isVisible()) window.showInactive() | ||
| } catch (e) { | ||
| Log.warning('error during resizing ChatIslandWindow:', e) | ||
| } | ||
| } | ||
|
|
||
| private workArea() { | ||
| const window = this.window.getWindow() | ||
| if (window?.isVisible()) | ||
| return screen.getDisplayMatching(window.getBounds()).workArea | ||
| // Hidden: back on the display it was dragged to, while it is still connected. | ||
| const d = screen.getAllDisplays().find((d) => d.id === this.displayId) | ||
| return (d ?? screen.getPrimaryDisplay()).workArea | ||
| } | ||
|
|
||
| // After a drag: the corner nearest the dock is where the island grows from, as in the CTI. | ||
| keepPosition() { | ||
| const window = this.window.getWindow() | ||
| if (!window) return | ||
| const b = window.getBounds() | ||
| this.displayId = screen.getDisplayMatching(b).id | ||
| const { x, y, width, height } = this.workArea() | ||
| // The dock sits in the window's corner: its place follows from the bounds, only its size is measured. | ||
| const w = this.size.dock?.w ?? b.width - 2 * SIDE | ||
| const h = this.size.dock?.h ?? b.height - 2 * END | ||
| const dock = { | ||
| x: this.corner.endsWith('left') ? b.x + SIDE : b.x + b.width - SIDE - w, | ||
| y: this.corner.startsWith('top') ? b.y + END : b.y + b.height - END - h, | ||
| w, | ||
| h, | ||
| } | ||
| const left = dock.x + dock.w / 2 < x + width / 2 | ||
| const top = dock.y + dock.h / 2 < y + height / 2 | ||
| const corner: Corner = `${top ? 'top' : 'bottom'}-${left ? 'left' : 'right'}` | ||
| this.anchor = { | ||
| x: Math.max( | ||
| 0, | ||
| left ? dock.x - SIDE - x : x + width - dock.x - dock.w - SIDE, | ||
| ), | ||
| y: Math.max( | ||
| 0, | ||
| top ? dock.y - END - y : y + height - dock.y - dock.h - END, | ||
| ), | ||
| } | ||
| if (corner !== this.corner) { | ||
| this.corner = corner | ||
| this.window.emit(IPC_EVENTS.CHAT_TO_ISLAND, 'chat-island-corner', corner) | ||
| this.resize(this.size) | ||
| } | ||
| } | ||
|
|
||
| // Focus once the island has sized the window for what it opens. | ||
| focus() { | ||
| const window = this.window.getWindow() | ||
| if (window?.isVisible()) window.focus() | ||
| // Only for the resize this open causes: a later one (a message growing the dock) must not steal focus. | ||
| this.wantFocus = true | ||
| clearTimeout(this.focusTimer) | ||
| this.focusTimer = setTimeout(() => (this.wantFocus = false), 1000) | ||
| } | ||
|
|
||
| async safeQuit() { | ||
| ChatIslandController.instance = undefined | ||
| await this.window.quit(true) | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| import { PAGES } from '@shared/types' | ||
| import { BaseWindow } from './BaseWindow' | ||
|
|
||
| // Transparent, always on top: the chat stays in view with NethLink closed. | ||
| export class ChatIslandWindow extends BaseWindow { | ||
| constructor() { | ||
| super(PAGES.CHATISLAND, { | ||
| width: 440, | ||
| height: 600, | ||
| show: false, | ||
| focusable: true, | ||
| acceptFirstMouse: true, | ||
| fullscreenable: false, | ||
| autoHideMenuBar: true, | ||
| closable: false, | ||
| alwaysOnTop: true, | ||
| minimizable: false, | ||
| maximizable: false, | ||
| movable: false, | ||
| resizable: false, | ||
| skipTaskbar: true, | ||
| roundedCorners: false, | ||
| parent: undefined, | ||
| transparent: true, | ||
| hiddenInMissionControl: true, | ||
| hasShadow: false, | ||
| fullscreen: false, | ||
| enableLargerThanScreen: false, | ||
| frame: false, | ||
| thickFrame: false, | ||
| trafficLightPosition: { x: 0, y: 0 }, | ||
| webPreferences: { | ||
| nodeIntegration: true, | ||
| backgroundThrottling: false, | ||
| }, | ||
| }) | ||
|
|
||
| this.addOnBuildListener(() => { | ||
| this.getWindow()?.setAlwaysOnTop(true, 'screen-saver') | ||
| }) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.