Skip to content

Commit 586a20b

Browse files
authored
Named urls and start implementing a custom window toolbar on Windows (#101)
* Hide the edited label from the header if the title isn't supposed to be shown Signed-off-by: Axel Boberg <git@axelboberg.se> * Refactor and add support for transparency to child windows Signed-off-by: Axel Boberg <git@axelboberg.se> * Add window controls on windows and remove the window frame Signed-off-by: Axel Boberg <git@axelboberg.se> * Identify windows correctly and style window close buttons more similar to native ones Signed-off-by: Axel Boberg <git@axelboberg.se> * Fix issues causing child windows to not work with the new close, maximize and minimize buttons on windows Signed-off-by: Axel Boberg <git@axelboberg.se> * Fix an issue causing the main window's close controls to become unresponsive after cancelling the save dialog Signed-off-by: Axel Boberg <git@axelboberg.se> * Add a missing parameter preventing the local state from updating Signed-off-by: Axel Boberg <git@axelboberg.se> * Fix an issue with authorization of the window api that didn't correctly deny a request Signed-off-by: Axel Boberg <git@axelboberg.se> * Hide header window controls if not on windows Signed-off-by: Axel Boberg <git@axelboberg.se> * Fix incorrect padding if running in the browser on windows Signed-off-by: Axel Boberg <git@axelboberg.se> * Update rules to work with all window commands Signed-off-by: Axel Boberg <git@axelboberg.se> * Make sure item context menus stop propagation, fix issues where a multi selection wasn't properly identified and add play and stop buttons Signed-off-by: Axel Boberg <git@axelboberg.se> * Overlay native window controls on top of the ones rendered by the frontend on Windows Signed-off-by: Axel Boberg <git@axelboberg.se> * Begin conversion to strictly native window controls and make sure that child windows update their theming when the parent window changes Signed-off-by: Axel Boberg <git@axelboberg.se> * Set the windows native window controls each time the local theme is loaded Signed-off-by: Axel Boberg <git@axelboberg.se> * Perform a feature check before trying to set the titleBarOverlay colors Signed-off-by: Axel Boberg <git@axelboberg.se> * Start adding an app menu on windows Signed-off-by: Axel Boberg <git@axelboberg.se> * Add named urls for workspaces Signed-off-by: Axel Boberg <git@axelboberg.se> * Update changelog Signed-off-by: Axel Boberg <git@axelboberg.se> * Update copy for shared url preferences Signed-off-by: Axel Boberg <git@axelboberg.se> --------- Signed-off-by: Axel Boberg <git@axelboberg.se>
1 parent 0d10088 commit 586a20b

27 files changed

Lines changed: 659 additions & 92 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 1.0.0-beta.9 - [UNRELEASED]
4+
### Added
5+
- Support for named urls when sharing links to workspaces
6+
37
## 1.0.0-beta.8
48
### Fixed
59
- The space key can now be used for keyboard shortcuts

app/App.jsx

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { SocketContext } from './socketContext'
1414
import { useWebsocket } from './hooks/useWebsocket'
1515

1616

17+
import * as windowUtils from './utils/window'
1718
import * as shortcuts from './utils/shortcuts'
1819
import * as browser from './utils/browser'
1920
import * as auth from './auth'
@@ -68,8 +69,29 @@ root html tag for platform-specific styling e.t.c.
6869
*/
6970
;(function () {
7071
window.document.documentElement.dataset.platform = browser.platform()
72+
window.document.documentElement.dataset.agent = browser.isElectron() ? 'electron' : 'web'
7173
})()
7274

75+
;(async function () {
76+
const token = await auth.getToken()
77+
const bridge = await api.load()
78+
bridge.commands.setHeader('authentication', token)
79+
})()
80+
81+
async function updateControlsColors () {
82+
/*
83+
Wait for authentication
84+
as the setControlColors
85+
would be blocked without it
86+
*/
87+
await auth.getToken()
88+
89+
const style = getComputedStyle(document.body)
90+
windowUtils.setControlColors({
91+
symbolColor: style.getPropertyValue('--base-color')
92+
})
93+
}
94+
7395
const websocketQuery = {
7496
workspace
7597
}
@@ -127,16 +149,6 @@ export default function App () {
127149
setup()
128150
}, [readyState])
129151

130-
React.useEffect(() => {
131-
async function setup () {
132-
const token = await auth.getToken()
133-
const bridge = await api.load()
134-
bridge.commands.setHeader('authentication', token)
135-
}
136-
if (readyState !== 1) return
137-
setup()
138-
}, [readyState])
139-
140152
/**
141153
* Apply data to the shared state,
142154
* this will send a partial update
@@ -192,6 +204,14 @@ export default function App () {
192204
applyLocal({ appliedTheme: local.theme })
193205
}, [local.theme])
194206

207+
/*
208+
Also notify the main thread to
209+
update the window controls
210+
*/
211+
React.useEffect(() => {
212+
updateControlsColors()
213+
}, [local.appliedTheme])
214+
195215
/*
196216
Load the theme from localstorage
197217
into the local context
@@ -201,6 +221,27 @@ export default function App () {
201221
applyLocal({ theme })
202222
}, [])
203223

224+
/*
225+
Listen to changes to localstorage to update
226+
the current window if the theme changes in another window
227+
228+
Note that the event won't fire in the same
229+
window that set the local storage item
230+
*/
231+
React.useEffect(() => {
232+
function onStorageChange (e) {
233+
if (e.key === 'bridge.theme') {
234+
applyLocal({
235+
theme: e.newValue
236+
})
237+
}
238+
}
239+
window.addEventListener('storage', onStorageChange)
240+
return () => {
241+
window.removeEventListener('storage', onStorageChange)
242+
}
243+
}, [])
244+
204245
return (
205246
<SocketContext.Provider value={[send, data]}>
206247
<LocalContext.Provider value={[local, applyLocal]}>

app/assets/icons/index.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ import editDetail from './edit-detail.svg'
1919
import placeholder from './placeholder.svg'
2020
import preferences from './preferences.svg'
2121

22+
import windowClose from './window-close.svg'
23+
import windowRestore from './window-restore.svg'
24+
import windowMaximize from './window-maximize.svg'
25+
import windowMinimize from './window-minimize.svg'
26+
2227
import colorSuccess from './color-success.svg'
2328
import colorWarning from './color-warning.svg'
2429

@@ -40,5 +45,9 @@ export default {
4045
placeholder,
4146
preferences,
4247
colorSuccess,
43-
colorWarning
48+
colorWarning,
49+
windowClose,
50+
windowRestore,
51+
windowMaximize,
52+
windowMinimize
4453
}

app/assets/icons/window-close.svg

Lines changed: 7 additions & 0 deletions
Loading
Lines changed: 7 additions & 0 deletions
Loading
Lines changed: 7 additions & 0 deletions
Loading
Lines changed: 8 additions & 0 deletions
Loading
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.AppMenuRootItem {
2+
padding: 0.3em 0.8em;
3+
border-radius: 7px;
4+
}
5+
6+
.AppMenuRootItem:hover,
7+
.AppMenuRootItem:focus {
8+
background: var(--base-color--shade);
9+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import React from 'react'
2+
import * as api from '../../api'
3+
4+
import './AppMenuRootItem.css'
5+
6+
const MENU_MARGIN_PX = 10
7+
8+
export function AppMenuRootItem ({ label, spec }) {
9+
const elRef = React.useRef()
10+
11+
async function handleClick (e) {
12+
const bounds = e.target.getBoundingClientRect()
13+
14+
const x = e.screenX - (e.clientX - bounds.x)
15+
const y = e.screenY - (e.clientY - bounds.y) + bounds.height + MENU_MARGIN_PX
16+
17+
const bridge = await api.load()
18+
bridge.ui.contextMenu.open(spec, {
19+
x,
20+
y
21+
})
22+
}
23+
24+
return (
25+
<div ref={elRef} className='AppMenuRootItem' onClick={e => handleClick(e)}>
26+
{label}
27+
</div>
28+
)
29+
}

app/components/AppMenu/index.jsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from 'react'
2+
import * as api from '../../api'
3+
4+
import './style.css'
5+
6+
import { AppMenuRootItem } from './AppMenuRootItem'
7+
8+
import * as windowUtils from '../../utils/window'
9+
10+
function recursivePopulateCommandsInPlace (spec) {
11+
for (const item of spec) {
12+
if (typeof item?.command === 'string') {
13+
item.onClick = async () => {
14+
const bridge = await api.load()
15+
bridge.commands.executeRawCommand(item.command, windowUtils.getWindowId())
16+
}
17+
}
18+
if (Array.isArray(item.children)) {
19+
recursivePopulateCommandsInPlace(item.children)
20+
}
21+
}
22+
}
23+
24+
export function AppMenu () {
25+
const [menu, setMenu] = React.useState()
26+
27+
React.useEffect(() => {
28+
async function getMenu () {
29+
const bridge = await api.load()
30+
const menu = await bridge.commands.executeCommand('window.getAppMenu')
31+
recursivePopulateCommandsInPlace(menu)
32+
setMenu(menu)
33+
}
34+
getMenu()
35+
}, [])
36+
37+
return (
38+
<div className='AppMenu'>
39+
{
40+
(menu || [])
41+
.filter(item => item?.label && item?.children?.length > 0)
42+
.map(item => {
43+
return (
44+
<AppMenuRootItem key={item?.label} label={item?.label} spec={item?.children} />
45+
)
46+
})
47+
}
48+
</div>
49+
)
50+
}

0 commit comments

Comments
 (0)