Add Settings to web app (#125)

* Add support for opening settings dialog in the webapp

* Add tests for settings dialog

* Hide stand-alone app specific settings in web app

* Remove debug log
This commit is contained in:
Jonatan Heyman 2024-01-04 14:27:04 +01:00 committed by GitHub
parent 0ba5820cf4
commit acb7ddf189
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 137 additions and 30 deletions

View File

@ -0,0 +1 @@
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="M262.29 192.31a64 64 0 1 0 57.4 57.4 64.13 64.13 0 0 0-57.4-57.4ZM416.39 256a154.34 154.34 0 0 1-1.53 20.79l45.21 35.46a10.81 10.81 0 0 1 2.45 13.75l-42.77 74a10.81 10.81 0 0 1-13.14 4.59l-44.9-18.08a16.11 16.11 0 0 0-15.17 1.75A164.48 164.48 0 0 1 325 400.8a15.94 15.94 0 0 0-8.82 12.14l-6.73 47.89a11.08 11.08 0 0 1-10.68 9.17h-85.54a11.11 11.11 0 0 1-10.69-8.87l-6.72-47.82a16.07 16.07 0 0 0-9-12.22 155.3 155.3 0 0 1-21.46-12.57 16 16 0 0 0-15.11-1.71l-44.89 18.07a10.81 10.81 0 0 1-13.14-4.58l-42.77-74a10.8 10.8 0 0 1 2.45-13.75l38.21-30a16.05 16.05 0 0 0 6-14.08c-.36-4.17-.58-8.33-.58-12.5s.21-8.27.58-12.35a16 16 0 0 0-6.07-13.94l-38.19-30A10.81 10.81 0 0 1 49.48 186l42.77-74a10.81 10.81 0 0 1 13.14-4.59l44.9 18.08a16.11 16.11 0 0 0 15.17-1.75A164.48 164.48 0 0 1 187 111.2a15.94 15.94 0 0 0 8.82-12.14l6.73-47.89A11.08 11.08 0 0 1 213.23 42h85.54a11.11 11.11 0 0 1 10.69 8.87l6.72 47.82a16.07 16.07 0 0 0 9 12.22 155.3 155.3 0 0 1 21.46 12.57 16 16 0 0 0 15.11 1.71l44.89-18.07a10.81 10.81 0 0 1 13.14 4.58l42.77 74a10.8 10.8 0 0 1-2.45 13.75l-38.21 30a16.05 16.05 0 0 0-6.05 14.08c.33 4.14.55 8.3.55 12.47Z" fill="none" stroke="#ffffff" stroke-linecap="round" stroke-linejoin="round" stroke-width="32px" class="stroke-000000"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -25,6 +25,7 @@ contextBridge.exposeInMainWorld("heynote", {
isWindows, isWindows,
isLinux, isLinux,
}, },
isWebApp: false,
themeMode: themeMode, themeMode: themeMode,

View File

@ -135,6 +135,7 @@
@toggleTheme="toggleTheme" @toggleTheme="toggleTheme"
@openLanguageSelector="openLanguageSelector" @openLanguageSelector="openLanguageSelector"
@formatCurrentBlock="formatCurrentBlock" @formatCurrentBlock="formatCurrentBlock"
@openSettings="showSettings = true"
class="status" class="status"
/> />
<div class="overlay"> <div class="overlay">

View File

@ -22,6 +22,12 @@
UpdateStatusItem, UpdateStatusItem,
}, },
data() {
return {
showSettingsButton: window.heynote.isWebApp,
}
},
mounted() { mounted() {
}, },
@ -90,6 +96,14 @@
:autoUpdate="autoUpdate" :autoUpdate="autoUpdate"
:allowBetaVersions="allowBetaVersions" :allowBetaVersions="allowBetaVersions"
/> />
<div
v-if="showSettingsButton"
@click="$emit('openSettings')"
class="status-block settings clickable"
title="Settings"
>
<span class="icon icon-format"></span>
</div>
<div class="status-block theme clickable" @click="$emit('toggleTheme')" title="Toggle dark/light mode"> <div class="status-block theme clickable" @click="$emit('toggleTheme')" title="Toggle dark/light mode">
<span :class="'icon ' + themeSetting"></span> <span :class="'icon ' + themeSetting"></span>
</div> </div>
@ -132,6 +146,12 @@
cursor: pointer cursor: pointer
&:hover &:hover
background-color: rgba(255,255,255, 0.1) background-color: rgba(255,255,255, 0.1)
.icon
display: block
width: 14px
height: 22px
+dark-mode
opacity: 0.9
.line-number .line-number
color: rgba(255, 255, 255, 0.7) color: rgba(255, 255, 255, 0.7)
.num .num
@ -148,14 +168,9 @@
padding-top: 0 padding-top: 0
padding-bottom: 0 padding-bottom: 0
.icon .icon
display: block
width: 14px
height: 22px
background-size: 14px background-size: 14px
background-repeat: no-repeat background-repeat: no-repeat
background-position: center center background-position: center center
+dark-mode
opacity: 0.9
&.dark &.dark
background-image: url("@/assets/icons/dark-mode.png") background-image: url("@/assets/icons/dark-mode.png")
&.light &.light
@ -167,14 +182,18 @@
padding-top: 0 padding-top: 0
padding-bottom: 0 padding-bottom: 0
.icon .icon
display: block
width: 14px
height: 22px
+dark-mode
opacity: 0.9
background-size: 16px background-size: 16px
background-repeat: no-repeat background-repeat: no-repeat
background-position: center center background-position: center center
background-image: url("@/assets/icons/format.svg") background-image: url("@/assets/icons/format.svg")
.settings
padding-top: 0
padding-bottom: 0
.icon
background-size: 13px
background-repeat: no-repeat
background-position: center center
background-image: url("@/assets/icons/settings.svg")
</style> </style>

View File

@ -33,6 +33,7 @@
autoUpdate: this.initialSettings.autoUpdate, autoUpdate: this.initialSettings.autoUpdate,
activeTab: "general", activeTab: "general",
isWebApp: window.heynote.isWebApp,
} }
}, },
@ -89,6 +90,7 @@
@click="activeTab = 'appearance'" @click="activeTab = 'appearance'"
/> />
<TabListItem <TabListItem
v-if="!isWebApp"
name="Updates" name="Updates"
tab="updates" tab="updates"
:activeTab="activeTab" :activeTab="activeTab"
@ -115,7 +117,7 @@
</select> </select>
</div> </div>
</div> </div>
<div class="row"> <div class="row" v-if="!isWebApp">
<div class="entry"> <div class="entry">
<h2>Global Keyboard Shortcut</h2> <h2>Global Keyboard Shortcut</h2>
<label class="keyboard-shortcut-label"> <label class="keyboard-shortcut-label">
@ -134,7 +136,7 @@
/> />
</div> </div>
</div> </div>
<div class="row"> <div class="row" v-if="!isWebApp">
<div class="entry"> <div class="entry">
<h2>Show In</h2> <h2>Show In</h2>
<label v-if="isMac"> <label v-if="isMac">
@ -183,7 +185,7 @@
</div> </div>
</TabContent> </TabContent>
<TabContent tab="updates" :activeTab="activeTab"> <TabContent tab="updates" :activeTab="activeTab" v-if="!isWebApp">
<div class="row"> <div class="row">
<div class="entry"> <div class="entry">
<h2>Auto Update</h2> <h2>Auto Update</h2>

View File

@ -4,7 +4,7 @@
computed: { computed: {
className() { className() {
return "tab-content " + (this.tab === this.activeTab ? "active" : "") return "tab-content tab-" + this.tab + " " + (this.tab === this.activeTab ? "active" : "")
} }
} }
} }

View File

@ -4,7 +4,7 @@
computed: { computed: {
tabClass() { tabClass() {
return this.tab === this.activeTab ? "active" : "" return "tab-" + this.tab + " " + (this.tab === this.activeTab ? "active" : "")
} }
} }
} }

View File

@ -4,7 +4,6 @@ import { HeynotePage } from "./test-utils.js";
let heynotePage let heynotePage
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
console.log("beforeEach")
heynotePage = new HeynotePage(page) heynotePage = new HeynotePage(page)
await heynotePage.goto() await heynotePage.goto()
}); });

View File

@ -4,7 +4,6 @@ import { HeynotePage } from "./test-utils.js";
let heynotePage let heynotePage
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
console.log("beforeEach")
heynotePage = new HeynotePage(page) heynotePage = new HeynotePage(page)
await heynotePage.goto() await heynotePage.goto()
}) })

View File

@ -4,7 +4,6 @@ import { HeynotePage } from "./test-utils.js";
let heynotePage let heynotePage
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
console.log("beforeEach")
heynotePage = new HeynotePage(page) heynotePage = new HeynotePage(page)
await heynotePage.goto() await heynotePage.goto()
}) })

View File

@ -4,7 +4,6 @@ import { HeynotePage } from "./test-utils.js";
let heynotePage let heynotePage
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
console.log("beforeEach")
heynotePage = new HeynotePage(page) heynotePage = new HeynotePage(page)
await heynotePage.goto() await heynotePage.goto()
}); });

View File

@ -4,7 +4,6 @@ import { HeynotePage } from "./test-utils.js";
let heynotePage let heynotePage
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
console.log("beforeEach")
heynotePage = new HeynotePage(page) heynotePage = new HeynotePage(page)
await heynotePage.goto() await heynotePage.goto()
}); });

40
tests/settings.spec.js Normal file
View File

@ -0,0 +1,40 @@
import { test, expect } from "@playwright/test";
import { HeynotePage } from "./test-utils.js";
let heynotePage
test.beforeEach(async ({ page }) => {
heynotePage = new HeynotePage(page)
await heynotePage.goto()
});
test("test open settings dialog", async ({ page }) => {
await page.locator("css=.status-block.settings").click()
await expect(page.locator("css=.overlay .settings .dialog")).toBeVisible()
})
test("test close settings dialog", async ({ page }) => {
await page.locator("css=.status-block.settings").click()
await page.locator("css=.overlay .settings .dialog .bottom-bar .close").click()
await expect(page.locator("css=.overlay .settings .dialog")).not.toBeVisible()
})
test("test close settings dialog with escape", async ({ page }) => {
await page.locator("css=.status-block.settings").click()
await page.locator("body").press("Escape")
await expect(page.locator("css=.overlay .settings .dialog")).not.toBeVisible()
})
test("test change line gutter setting", async ({ page }) => {
await expect(page.locator("css=.cm-lineNumbers")).toBeVisible()
await page.locator("css=.status-block.settings").click()
await page.locator("css=.overlay .settings .dialog .sidebar li.tab-appearance").click()
await expect(page.locator("css=.settings .tab-content.tab-appearance")).toBeVisible()
await page.getByLabel("Show line numbers").click()
await expect(page.locator("css=.cm-lineNumbers")).toBeHidden()
expect((await heynotePage.getStoredSettings()).showLineNumberGutter).toBe(false)
await page.getByLabel("Show line numbers").click()
await expect(page.locator("css=.cm-lineNumbers")).toBeVisible()
expect((await heynotePage.getStoredSettings()).showLineNumberGutter).toBe(true)
})

View File

@ -42,4 +42,8 @@ export class HeynotePage {
const block = blocks[blockIndex] const block = blocks[blockIndex]
return content.slice(block.content.from, block.content.to) return content.slice(block.content.from, block.content.to)
} }
async getStoredSettings() {
return await this.page.evaluate(() => JSON.parse(window.localStorage.getItem("settings")))
}
} }

View File

@ -1,3 +1,5 @@
import { SETTINGS_CHANGE_EVENT, OPEN_SETTINGS_EVENT } from "../electron/constants";
const mediaMatch = window.matchMedia('(prefers-color-scheme: dark)') const mediaMatch = window.matchMedia('(prefers-color-scheme: dark)')
let themeCallback = null let themeCallback = null
mediaMatch.addEventListener("change", async (event) => { mediaMatch.addEventListener("change", async (event) => {
@ -31,8 +33,46 @@ if (uaPlatform.indexOf("Win") !== -1) {
} }
} }
class IpcRenderer {
constructor() {
this.callbacks = {}
}
on(event, callback) {
if (!this.callbacks[event]) {
this.callbacks[event] = []
}
this.callbacks[event].push(callback)
}
send(event, ...args) {
if (this.callbacks[event]) {
for (const callback of this.callbacks[event]) {
callback(null, ...args)
}
}
}
}
const ipcRenderer = new IpcRenderer()
// get initial settings
let settingsData = localStorage.getItem("settings")
let initialSettings = {
keymap: "default",
emacsMetaKey: "meta",
showLineNumberGutter: true,
showFoldGutter: true,
}
if (settingsData !== null) {
initialSettings = Object.assign(initialSettings, JSON.parse(settingsData))
}
const Heynote = { const Heynote = {
platform: platform, platform: platform,
isWebApp: true,
buffer: { buffer: {
async load() { async load() {
@ -57,12 +97,19 @@ const Heynote = {
//ipcRenderer.on(WINDOW_CLOSE_EVENT, callback) //ipcRenderer.on(WINDOW_CLOSE_EVENT, callback)
}, },
settings: initialSettings,
onOpenSettings(callback) { onOpenSettings(callback) {
//ipcRenderer.on(OPEN_SETTINGS_EVENT, callback) ipcRenderer.on(OPEN_SETTINGS_EVENT, callback)
}, },
onSettingsChange(callback) { onSettingsChange(callback) {
//ipcRenderer.on(SETTINGS_CHANGE_EVENT, (event, settings) => callback(settings)) ipcRenderer.on(SETTINGS_CHANGE_EVENT, (event, settings) => callback(settings))
},
setSettings(settings) {
localStorage.setItem("settings", JSON.stringify(settings))
ipcRenderer.send(SETTINGS_CHANGE_EVENT, settings)
}, },
themeMode: { themeMode: {
@ -88,10 +135,6 @@ const Heynote = {
initial: localStorage.getItem("theme") || "system", initial: localStorage.getItem("theme") || "system",
}, },
settings: {
keymap: "default",
},
getCurrencyData: async () => { getCurrencyData: async () => {
if (currencyData !== null) { if (currencyData !== null) {
return currencyData return currencyData
@ -102,4 +145,4 @@ const Heynote = {
}, },
} }
export default Heynote export { Heynote, ipcRenderer}

View File

@ -11,8 +11,9 @@
<div id="app"></div> <div id="app"></div>
<script type="module"> <script type="module">
import heynote from "./bridge.js"; import { Heynote, ipcRenderer } from "./bridge.js";
window.heynote = heynote; window.ipcRenderer = ipcRenderer;
window.heynote = Heynote;
</script> </script>
<script type="module" src="main.js"></script> <script type="module" src="main.js"></script>
<script src="./math.js"></script> <script src="./math.js"></script>