diff --git a/dist/index.css b/dist/index.css index 9b0eebc0..4984431e 100644 --- a/dist/index.css +++ b/dist/index.css @@ -172,14 +172,12 @@ body[theme=dark] [color]:not([color=standard]).subtle-background { } #background-wrapper { position: fixed; - top: 0px; - left: var(--safe-area-left); - right: var(--safe-area-right); - bottom: 0px; + inset: -80px; z-index: 1; pointer-events: none; overflow: hidden; opacity: 0.4; + filter: blur(40px); } main { z-index: 2; @@ -187,24 +185,20 @@ main { #background-wrapper div { content: ""; position: absolute; - top: -80px; - left: -80px; - right: -80px; - bottom: -80px; + inset: 0px; z-index: -1; pointer-events: none; - filter: blur(40px); } #background-wrapper #sky { left: -100%; bottom: -50%; - --color-1: hsl(188, 100%, 83%); - --color-2: hsl(194, 72%, 56%); - background-image: radial-gradient(var(--color-1), var(--color-2)); + background-image: radial-gradient(var(--sky-1), var(--sky-2)); } #background-wrapper #grass-1 { - --color-1: hsl(150, 100%, 30%); - --color-2: hsl(150, 100%, 25%); + --color-1: hsl(150, 60%, 30%); + --color-2: hsl(150, 60%, 25%); + filter: var(--backdrop-grass-filter); + opacity: .7; background-image: linear-gradient( 350deg, @@ -213,8 +207,10 @@ main { transparent 40%); } #background-wrapper #grass-2 { - --color-1: hsl(110, 100%, 40%); - --color-2: hsl(110, 100%, 35%); + --color-1: hsl(110, 60%, 40%); + --color-2: hsl(110, 60%, 35%); + filter: var(--backdrop-grass-filter); + opacity: .7; background-image: linear-gradient( 9deg, diff --git a/dist/index.js b/dist/index.js index 450bf304..904ac39f 100644 --- a/dist/index.js +++ b/dist/index.js @@ -353,6 +353,7 @@ info: "About Comms" }, themes: { + dynamic: "Dynamic", dark: "Dark", light: "Light", system: "Device theme" @@ -586,6 +587,7 @@ info: "\xDCber Comms" }, themes: { + dynamic: "Dynamisch", dark: "Dunkel", light: "Hell", system: "Ger\xE4teeinstellung" @@ -801,6 +803,7 @@ info: "Sobre Comms" }, themes: { + dynamic: "Din\xE1mico", dark: "Oscuro", light: "Claro", system: "Seg\xFAn dispositivo" @@ -1257,7 +1260,7 @@ this.connectionModel = connectionModel2; this.chatListModel = chatListModel2; this.fileTransferModel = fileTransferModel2; - this.BUILD = "Build 26.09.09.A"; + this.BUILD = "Build 26.09.17.A"; // CONTEXT this.contextStack = /* @__PURE__ */ new Map(); this.closeContext = (contextId, fromHistoryEvent = false) => { @@ -1800,10 +1803,42 @@ // view this.applyTheme = () => { let theme = this.theme.value; + if (theme == "dynamic" /* Dynamic */) { + const scene = this.selectDynamicScene(); + this.setScene(scene); + return; + } if (theme == "system" /* System */) { theme = _SettingsViewModel.getSystemTheme(); } - document.body.setAttribute("theme", theme); + if (theme == "dark" /* Dark */) { + this.setScene(DynamicSceneNight); + } else { + this.setScene(DynamicSceneDay); + } + }; + this.selectDynamicScene = () => { + let hour = (/* @__PURE__ */ new Date()).getHours(); + if (hour >= 21 || hour <= 6) { + return DynamicSceneNight; + } else if (hour > 6 && hour < 10) { + return DynamicSceneSunrise; + } else if (hour < 14) { + return DynamicSceneDay; + } else if (hour < 17) { + return DynamicSceneAfternoon; + } else if (hour < 21) { + return DynamicSceneSunset; + } + }; + this.setScene = (scene) => { + document.body.style.setProperty("--backdrop-grass-filter", `brightness(${scene.brightness})`); + function setSkyColor(tone, hue, saturation, luma) { + document.body.style.setProperty(`--sky-${tone}`, `hsl(${hue}, ${saturation}%, ${luma}%)`); + } + setSkyColor(1, scene.hue1, scene.saturation, scene.luma); + setSkyColor(2, scene.hue2, scene.saturation - 10, scene.luma - 10); + document.body.setAttribute("theme", scene.baseTheme); }; // exit this.close = () => { @@ -1835,6 +1870,11 @@ "change", () => this.applyTheme() ); + const seconds = (/* @__PURE__ */ new Date()).getSeconds(); + const secondsUntilNewMinute = 60 - seconds; + setTimeout(() => { + setInterval(this.applyTheme, 1e3 * 60); + }, secondsUntilNewMinute); this.registerKeyStroke("backspace" /* CloseOrCancel */, this.close); } static generateThemeMedia() { @@ -1845,6 +1885,46 @@ return media.matches == true ? "dark" /* Dark */ : "light" /* Light */; } }; + var DynamicSceneNight = { + hue1: 215, + hue2: 220, + saturation: 50, + luma: 25, + brightness: 0.4, + baseTheme: "dark" /* Dark */ + }; + var DynamicSceneSunrise = { + hue1: 45, + hue2: 190, + saturation: 100, + luma: 50, + brightness: 1.2, + baseTheme: "light" /* Light */ + }; + var DynamicSceneDay = { + hue1: 190, + hue2: 180, + saturation: 100, + luma: 70, + brightness: 1.4, + baseTheme: "light" /* Light */ + }; + var DynamicSceneAfternoon = { + hue1: 195, + hue2: 220, + saturation: 100, + luma: 50, + brightness: 0.8, + baseTheme: "light" /* Light */ + }; + var DynamicSceneSunset = { + hue1: 20, + hue2: 260, + saturation: 100, + luma: 50, + brightness: 0.4, + baseTheme: "dark" /* Dark */ + }; // src/ViewModel/Global/fileTransferViewModel.ts var FileTransferViewModel = class extends Context { @@ -6867,6 +6947,10 @@ function SettingsAppearancePane(coreViewModel2, settingsViewModel2) { return /* @__PURE__ */ createElement("div", { class: "slide-up" }, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.settings.pages.appearance), /* @__PURE__ */ createElement("hr", null), OptionButtonList( new ListState([ + [ + coreViewModel2.translations.settings.themes.dynamic, + "dynamic" /* Dynamic */ + ], [ coreViewModel2.translations.settings.themes.dark, "dark" /* Dark */ diff --git a/src/Model/Global/.settingsModel.ts.swp b/src/Model/Global/.settingsModel.ts.swp new file mode 100644 index 00000000..ec897c99 Binary files /dev/null and b/src/Model/Global/.settingsModel.ts.swp differ diff --git a/src/Model/Global/settingsModel.ts b/src/Model/Global/settingsModel.ts index a2b9128f..a2935ceb 100644 --- a/src/Model/Global/settingsModel.ts +++ b/src/Model/Global/settingsModel.ts @@ -106,4 +106,5 @@ export enum ThemeSettings { Dark = "dark", Light = "light", System = "system", + Dynamic = "dynamic", } diff --git a/src/View/Modals/.settingsModal.tsx.swp b/src/View/Modals/.settingsModal.tsx.swp new file mode 100644 index 00000000..191c651b Binary files /dev/null and b/src/View/Modals/.settingsModal.tsx.swp differ diff --git a/src/View/Modals/settingsModal.tsx b/src/View/Modals/settingsModal.tsx index a5cd8613..5156f143 100644 --- a/src/View/Modals/settingsModal.tsx +++ b/src/View/Modals/settingsModal.tsx @@ -170,6 +170,10 @@ function SettingsAppearancePane(
{OptionButtonList( new React.ListState<[string, string]>([ + [ + coreViewModel.translations.settings.themes.dynamic, + ThemeSettings.Dynamic, + ], [ coreViewModel.translations.settings.themes.dark, ThemeSettings.Dark, diff --git a/src/View/translations.ts b/src/View/translations.ts index ffae625e..289973f7 100644 --- a/src/View/translations.ts +++ b/src/View/translations.ts @@ -83,6 +83,7 @@ const englishTranslations = { info: "About Comms", }, themes: { + dynamic: "Dynamic", dark: "Dark", light: "Light", system: "Device theme", @@ -410,58 +411,59 @@ export const allTranslations: { info: "Über Comms", }, themes: { - dark: "Dunkel", - light: "Hell", - system: "Geräteeinstellung", - }, + dynamic: "Dynamisch", + dark: "Dunkel", + light: "Hell", + system: "Geräteeinstellung", + }, - version: "Version", - language: "Sprache", + version: "Version", + language: "Sprache", - firstDayOfWeekLabel: "Erster Wochentag", - }, + firstDayOfWeekLabel: "Erster Wochentag", + }, - connectionModal: { - connectionModalHeadline: "Verbindungen verwalten", + connectionModal: { + connectionModalHeadline: "Verbindungen verwalten", - connectButtonAudioLabel: "verbinden", - }, + connectButtonAudioLabel: "verbinden", + }, - dataTransferModal: { - transferDataHeadline: "Datenübertragung", - sendHeadline: "An anderes Gerät senden", - receiveHeadline: "An dieses Gerät senden", - exportHeadline: "Daten exportieren", - importHeadline: "Daten importieren", - selectionDescription: - "Wähle die Daten aus, die du übertragen möchtest.", - exportSelectionDescription: - "Wähle die Daten aus, die du exportieren möchtest.", - dataEntryDescription: - "Gib diese Informationen auf dem anderen Gerät ein.", - dataEntryInputDescription: - "Gib die auf dem anderen Gerät angezeigten Informationen ein.", + dataTransferModal: { + transferDataHeadline: "Datenübertragung", + sendHeadline: "An anderes Gerät senden", + receiveHeadline: "An dieses Gerät senden", + exportHeadline: "Daten exportieren", + importHeadline: "Daten importieren", + selectionDescription: + "Wähle die Daten aus, die du übertragen möchtest.", + exportSelectionDescription: + "Wähle die Daten aus, die du exportieren möchtest.", + dataEntryDescription: + "Gib diese Informationen auf dem anderen Gerät ein.", + dataEntryInputDescription: + "Gib die auf dem anderen Gerät angezeigten Informationen ein.", - fromThisDeviceButton: "An anderes Gerät senden", - toThisDeviceButton: "An dieses Gerät senden", - exportButton: "Als Datei exportieren", - importButton: "Aus Datei importieren", + fromThisDeviceButton: "An anderes Gerät senden", + toThisDeviceButton: "An dieses Gerät senden", + exportButton: "Als Datei exportieren", + importButton: "Aus Datei importieren", - generalHeadline: "Allgemein", + generalHeadline: "Allgemein", - connectionData: "Verbindungsdaten", - settingsData: "Einstellungen", + connectionData: "Verbindungsdaten", + settingsData: "Einstellungen", - chatsHeadline: "Chats", + chatsHeadline: "Chats", - transferChannelHeadline: "Übertragungskanal", - transferKeyHeadline: "Schlüssel", + transferChannelHeadline: "Übertragungskanal", + transferKeyHeadline: "Schlüssel", - sendButton: "Senden", - sendAgainButton: "Erneut senden", + sendButton: "Senden", + sendAgainButton: "Erneut senden", - filesSentCount: (count) => `Dateien gesendet: ${count}.`, - allFilesSent: "Fertig.", + filesSentCount: (count) => `Dateien gesendet: ${count}.`, + allFilesSent: "Fertig.", filesReceivedCount: (count) => `Dateien empfangen: ${count}.`, @@ -705,6 +707,7 @@ export const allTranslations: { info: "Sobre Comms", }, themes: { + dynamic: "Dinámico", dark: "Oscuro", light: "Claro", system: "Según dispositivo", @@ -758,7 +761,7 @@ export const allTranslations: { allFilesSent: "Hecho.", filesReceivedCount: (count) => `Archivos recibidos: ${count}.`, - + exportKey: "Clave de encriptación", exportKeyConfirmation: "Confirmar clave", downloadFileButton: "Descargar", diff --git a/src/ViewModel/Global/.coreViewModel.ts.swo b/src/ViewModel/Global/.coreViewModel.ts.swo new file mode 100644 index 00000000..691f7bd6 Binary files /dev/null and b/src/ViewModel/Global/.coreViewModel.ts.swo differ diff --git a/src/ViewModel/Global/.coreViewModel.ts.swp b/src/ViewModel/Global/.coreViewModel.ts.swp index dda9cc0b..7895f9d7 100644 Binary files a/src/ViewModel/Global/.coreViewModel.ts.swp and b/src/ViewModel/Global/.coreViewModel.ts.swp differ diff --git a/src/ViewModel/Global/.settingsViewModel.ts.swp b/src/ViewModel/Global/.settingsViewModel.ts.swp new file mode 100644 index 00000000..26c65a60 Binary files /dev/null and b/src/ViewModel/Global/.settingsViewModel.ts.swp differ diff --git a/src/ViewModel/Global/coreViewModel.ts b/src/ViewModel/Global/coreViewModel.ts index 46e94a4c..e4ec314a 100644 --- a/src/ViewModel/Global/coreViewModel.ts +++ b/src/ViewModel/Global/coreViewModel.ts @@ -9,7 +9,7 @@ import ConnectionModel from "../../Model/Global/connectionModel"; import ChatListModel from "../../Model/Chat/chatListModel"; export default class CoreViewModel { - readonly BUILD = "Build 26.09.09.A"; + readonly BUILD = "Build 26.09.17.A"; translations: Translations; diff --git a/src/ViewModel/Global/settingsViewModel.ts b/src/ViewModel/Global/settingsViewModel.ts index f603d20a..aa8b1b41 100644 --- a/src/ViewModel/Global/settingsViewModel.ts +++ b/src/ViewModel/Global/settingsViewModel.ts @@ -48,74 +48,168 @@ export default class SettingsViewModel extends Context { }; showModalPage = (page: SettingsModalPages): void => { - this.selectedModalPage.value = page; + this.selectedModalPage.value = page; }; // view applyTheme = (): void => { - let theme: string = this.theme.value; - if (theme == ThemeSettings.System) { - theme = SettingsViewModel.getSystemTheme(); - } - document.body.setAttribute("theme", theme); + let theme: string = this.theme.value; + + if (theme == ThemeSettings.Dynamic) { + const scene = this.selectDynamicScene(); + this.setScene(scene); + return; + } + + if (theme == ThemeSettings.System) { + theme = SettingsViewModel.getSystemTheme(); + } + + if (theme == ThemeSettings.Dark) { + this.setScene(DynamicSceneNight); + } else { + this.setScene(DynamicSceneDay); + } }; + selectDynamicScene = (): DynamicScene => { + let hour = new Date().getHours(); + + if (hour >= 21 || hour <= 6) { + return DynamicSceneNight; + } else if (hour > 6 && hour < 10) { + return DynamicSceneSunrise; + } else if (hour < 14) { + return DynamicSceneDay; + } else if (hour < 17) { + return DynamicSceneAfternoon + } else if (hour < 21) { + return DynamicSceneSunset + } + } + + setScene = (scene: DynamicScene): void => { + document.body.style.setProperty("--backdrop-grass-filter", `brightness(${scene.brightness})`); + + function setSkyColor(tone: 1|2, hue: number, saturation: number, luma: number) { + document.body.style.setProperty(`--sky-${tone}`, `hsl(${hue}, ${saturation}%, ${luma}%)`); + } + setSkyColor(1, scene.hue1, scene.saturation, scene.luma); + setSkyColor(2, scene.hue2, scene.saturation - 10, scene.luma-10); + + document.body.setAttribute("theme", scene.baseTheme); + } + // exit close = (): void => { - this.coreViewModel.closeContext(this.contextId); + this.coreViewModel.closeContext(this.contextId); }; handleContextClose = (): void => { - this.isShowingSettingsModal.value = false; - if (this.requiresReload.value == true) { - window.location.reload(); - } + this.isShowingSettingsModal.value = false; + if (this.requiresReload.value == true) { + window.location.reload(); + } }; // init constructor(public readonly coreViewModel: CoreViewModel) { - super("settings"); + super("settings"); - this.username.value = coreViewModel.settingsModel.username; - this.usernameInput.value = coreViewModel.settingsModel.username; - this.firstDayOfWeek.value = coreViewModel.settingsModel.firstDayOfWeek; - this.language.value = coreViewModel.settingsModel.language; - this.theme.value = coreViewModel.settingsModel.theme; + this.username.value = coreViewModel.settingsModel.username; + this.usernameInput.value = coreViewModel.settingsModel.username; + this.firstDayOfWeek.value = coreViewModel.settingsModel.firstDayOfWeek; + this.language.value = coreViewModel.settingsModel.language; + this.theme.value = coreViewModel.settingsModel.theme; - // subscriptions - this.firstDayOfWeek.subscribe(this.setFirstDayofWeek); - this.language.subscribeSilent((newValue) => { - this.coreViewModel.settingsModel.setLanguage(newValue); - this.requiresReload.value = true; - }); - this.theme.subscribeSilent((newValue) => { - this.coreViewModel.settingsModel.setTheme(newValue); - }); + // subscriptions + this.firstDayOfWeek.subscribe(this.setFirstDayofWeek); + this.language.subscribeSilent((newValue) => { + this.coreViewModel.settingsModel.setLanguage(newValue); + this.requiresReload.value = true; + }); + this.theme.subscribeSilent((newValue) => { + this.coreViewModel.settingsModel.setTheme(newValue); + }); - // set theme - this.theme.subscribe(() => { - this.applyTheme(); - }); - SettingsViewModel.generateThemeMedia().addEventListener("change", () => - this.applyTheme(), - ); + // set theme + this.theme.subscribe(() => { + this.applyTheme(); + }); + SettingsViewModel.generateThemeMedia().addEventListener("change", () => + this.applyTheme(), + ); + const seconds = new Date().getSeconds(); + const secondsUntilNewMinute = 60-seconds; + setTimeout(() => { + setInterval(this.applyTheme, 1000*60); + }, secondsUntilNewMinute); - // keystrokes - this.registerKeyStroke(CommonKeys.CloseOrCancel, this.close); + // keystrokes + this.registerKeyStroke(CommonKeys.CloseOrCancel, this.close); } static generateThemeMedia(): MediaQueryList { - return window.matchMedia("(prefers-color-scheme: dark)"); + return window.matchMedia("(prefers-color-scheme: dark)"); } static getSystemTheme(): string { - const media = SettingsViewModel.generateThemeMedia(); - return media.matches == true ? ThemeSettings.Dark : ThemeSettings.Light; + const media = SettingsViewModel.generateThemeMedia(); + return media.matches == true ? ThemeSettings.Dark : ThemeSettings.Light; } } export enum SettingsModalPages { Appearance, - Regional, - Info, + Regional, + Info, +} + +export interface DynamicScene { + hue1: number; + hue2: number; + saturation: number; + luma: number; + brightness: number; + baseTheme: ThemeSettings; +} +export const DynamicSceneNight: DynamicScene = { + hue1: 215, + hue2: 220, + saturation : 50, + luma: 25, + brightness: 0.4, + baseTheme: ThemeSettings.Dark, +} +export const DynamicSceneSunrise: DynamicScene = { + hue1: 45, + hue2: 190, + saturation : 100, + luma: 50, + brightness: 1.2, + baseTheme: ThemeSettings.Light, +} +export const DynamicSceneDay: DynamicScene = { + hue1: 190, + hue2: 180, + saturation : 100, + luma: 70, + brightness: 1.4, + baseTheme: ThemeSettings.Light, +} +export const DynamicSceneAfternoon: DynamicScene = { + hue1: 195, + hue2: 220, + saturation : 100, + luma: 50, + brightness: 0.8, + baseTheme: ThemeSettings.Light, +} +export const DynamicSceneSunset: DynamicScene = { + hue1: 20, + hue2: 260, + saturation : 100, + luma: 50, + brightness: 0.4, + baseTheme: ThemeSettings.Dark, } diff --git a/src/base.css b/src/base.css index d6089b0f..5daa50bb 100644 --- a/src/base.css +++ b/src/base.css @@ -7,14 +7,10 @@ } /* Gradient */ - #background-wrapper { position: fixed; - top: 0px; - left: var(--safe-area-left); - right: var(--safe-area-right); - bottom: 0px; + inset: -80px; z-index: 1; pointer-events: none; @@ -22,6 +18,7 @@ overflow: hidden; opacity: 0.4; + filter: blur(40px); } main { @@ -32,29 +29,25 @@ main { content: ""; position: absolute; - top: -80px; - left: -80px; - right: -80px; - bottom: -80px; + inset: 0px; z-index: -1; pointer-events: none; - - filter: blur(40px); } #background-wrapper #sky { left: -100%; bottom: -50%; - --color-1: hsl(188, 100%, 83%); - --color-2: hsl(194, 72%, 56%); - background-image: radial-gradient(var(--color-1), var(--color-2)); + background-image: radial-gradient(var(--sky-1), var(--sky-2)); } #background-wrapper #grass-1 { - --color-1: hsl(150, 100%, 30%); - --color-2: hsl(150, 100%, 25%); + --color-1: hsl(150, 60%, 30%); + --color-2: hsl(150, 60%, 25%); + + filter: var(--backdrop-grass-filter); + opacity: .7; background-image: linear-gradient( 350deg, @@ -65,8 +58,11 @@ main { } #background-wrapper #grass-2 { - --color-1: hsl(110, 100%, 40%); - --color-2: hsl(110, 100%, 35%); + --color-1: hsl(110, 60%, 40%); + --color-2: hsl(110, 60%, 35%); + + filter: var(--backdrop-grass-filter); + opacity: .7; background-image: linear-gradient( 9deg,