This commit is contained in:
marlon-erler
2026-09-26 23:32:38 +02:00
parent ce9f6d89f0
commit 9622c196cd
40 changed files with 2866 additions and 2706 deletions
+7 -4
View File
@@ -72,10 +72,13 @@ export function ChatMessageInfoModal(
}
<span class="icon">key</span>
</button>
<button
on:click={chatMessageViewModel.reply}
toggle:disabled={chatMessageViewModel.messagePageViewModel.isReplyViewActive}
>
<button
on:click={chatMessageViewModel.reply}
toggle:disabled={
chatMessageViewModel.messagePageViewModel
.isReplyViewActive
}
>
{
coreViewModel.translations.chatPage.message
.replyToMessageButton
+95 -92
View File
@@ -1,10 +1,10 @@
import { v4 } from "uuid";
import { ViewController } from "../viewController";
import { StringToOption } from "../Components/option";
import { DeletableListItem } from "../Components/deletableListItem";
import * as React from "../../react";
import SearchViewModel from "../../ViewModel/Utility/searchViewModel";
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import {DeletableListItem} from "../Components/deletableListItem";
export function SearchModal<T>(
coreViewModel: CoreViewModel,
@@ -13,92 +13,92 @@ export function SearchModal<T>(
isOpen: React.State<boolean>,
) {
function close() {
isOpen.value = false;
isOpen.value = false;
}
const suggestionId = v4();
isOpen.subscribe((isOpen) => {
if (!isOpen) return;
ViewController.setFocusWithDelay();
if (!isOpen) return;
ViewController.setFocusWithDelay();
});
return (
<div class="modal" toggle:open={isOpen} extended>
<div>
<main>
<h2>{headline}</h2>
<div class="flex-row">
<input
id="focused"
style="max-width: unset"
placeholder={
coreViewModel.translations.general.searchLabel
}
bind:value={searchViewModel.searchInput}
on:enter={searchViewModel.applySearch}
list={suggestionId}
></input>
<datalist
hidden
id={suggestionId}
children:append={[
searchViewModel.suggestions,
StringToOption,
]}
></datalist>
<button
class="standard"
aria-label={
coreViewModel.translations.general
.searchButtonClearAudioLabel
}
on:click={searchViewModel.clear}
toggle:disabled={searchViewModel.cannotClear}
>
<span class="icon">close</span>
</button>
<button
class="primary"
aria-label={
coreViewModel.translations.general
.searchButtonAudioLabel
}
on:click={searchViewModel.applySearch}
toggle:disabled={searchViewModel.cannotApplySearch}
>
<span class="icon">search</span>
</button>
</div>
<div class="modal" toggle:open={isOpen} extended>
<div>
<main>
<h2>{headline}</h2>
<div class="flex-row">
<input
id="focused"
style="max-width: unset"
placeholder={
coreViewModel.translations.general.searchLabel
}
bind:value={searchViewModel.searchInput}
on:enter={searchViewModel.applySearch}
list={suggestionId}
></input>
<datalist
hidden
id={suggestionId}
children:append={[
searchViewModel.suggestions,
StringToOption,
]}
></datalist>
<button
class="standard"
aria-label={
coreViewModel.translations.general
.searchButtonClearAudioLabel
}
on:click={searchViewModel.clear}
toggle:disabled={searchViewModel.cannotClear}
>
<span class="icon">close</span>
</button>
<button
class="primary"
aria-label={
coreViewModel.translations.general
.searchButtonAudioLabel
}
on:click={searchViewModel.applySearch}
toggle:disabled={searchViewModel.cannotApplySearch}
>
<span class="icon">search</span>
</button>
</div>
<div toggle:hidden={searchViewModel.hasNoSuggestions}>
<hr></hr>
<h3>
{
coreViewModel.translations.general
.searchSuggestionsLabel
}
</h3>
<div
class="flex-column gap"
children:append={[
searchViewModel.suggestions,
(suggestion: string) =>
SuggestionView(
suggestion,
searchViewModel,
coreViewModel,
),
]}
></div>
</div>
</main>
<button on:click={close}>
{coreViewModel.translations.general.closeButton}
<span class="icon">close</span>
</button>
</div>
</div>
<div toggle:hidden={searchViewModel.hasNoSuggestions}>
<hr></hr>
<h3>
{
coreViewModel.translations.general
.searchSuggestionsLabel
}
</h3>
<div
class="flex-column gap"
children:append={[
searchViewModel.suggestions,
(suggestion: string) =>
SuggestionView(
suggestion,
searchViewModel,
coreViewModel,
),
]}
></div>
</div>
</main>
<button on:click={close}>
{coreViewModel.translations.general.closeButton}
<span class="icon">close</span>
</button>
</div>
</div>
);
}
@@ -108,26 +108,29 @@ function SuggestionView<T>(
coreViewModel: CoreViewModel,
) {
const isApplied = React.createProxyState(
[searchViewModel.appliedQuery],
() => searchViewModel.appliedQuery.value == suggestion,
[searchViewModel.appliedQuery],
() => searchViewModel.appliedQuery.value == suggestion,
);
function deleteSuggestion() {
searchViewModel.deleteSuggestion(suggestion);
searchViewModel.deleteSuggestion(suggestion);
}
function applySuggestion() {
searchViewModel.search(suggestion);
searchViewModel.search(suggestion);
}
return DeletableListItem(coreViewModel, suggestion,
<button
class="primary"
aria-label={coreViewModel.translations.general.applyButton}
on:click={applySuggestion}
toggle:disabled={isApplied}
>
<span class="icon">check</span>
</button>
, deleteSuggestion)
return DeletableListItem(
coreViewModel,
suggestion,
<button
class="primary"
aria-label={coreViewModel.translations.general.applyButton}
on:click={applySuggestion}
toggle:disabled={isApplied}
>
<span class="icon">check</span>
</button>,
deleteSuggestion,
);
}
+65 -70
View File
@@ -125,25 +125,20 @@ function SettingsInfoPane(
settingsViewModel.coreViewModel.version,
)}
<button
class="primary"
on:click={coreViewModel.update}
toggle:hidden={coreViewModel.noUpdateAvailable}
>
<span
subscribe:innerText={coreViewModel.updateText}
></span>
<span class="icon">update</span>
</button>
<button
class="primary"
on:click={coreViewModel.update}
toggle:hidden={coreViewModel.noUpdateAvailable}
>
<span subscribe:innerText={coreViewModel.updateText}></span>
<span class="icon">update</span>
</button>
<button
class="standard"
on:click={coreViewModel.checkUpdates}
>
{coreViewModel.translations.settings.checkUpdatesButton}
</button>
</div>
</div>
<button class="standard" on:click={coreViewModel.checkUpdates}>
{coreViewModel.translations.settings.checkUpdatesButton}
</button>
</div>
</div>
);
}
@@ -152,30 +147,30 @@ function SettingsRegionalPane(
settingsViewModel: SettingsViewModel,
) {
return (
<div class="slide-up">
<h2>{coreViewModel.translations.settings.pages.regional}</h2>
<div class="slide-up">
<h2>{coreViewModel.translations.settings.pages.regional}</h2>
<hr></hr>
<h3>{coreViewModel.translations.settings.language}</h3>
{OptionButtonList(
new React.ListState<[string, string]>(
Object.values(Languages).map((x) => [languageNames[x], x]),
),
settingsViewModel.language,
)}
<hr></hr>
<h3>{coreViewModel.translations.settings.language}</h3>
{OptionButtonList(
new React.ListState<[string, string]>(
Object.values(Languages).map((x) => [languageNames[x], x]),
),
settingsViewModel.language,
)}
<hr></hr>
<h3>{coreViewModel.translations.settings.firstDayOfWeekLabel}</h3>
{OptionButtonList(
new React.ListState<[string, string]>(
coreViewModel.translations.regional.weekdays.full.map(
(x, i) => [x, i.toString()],
),
),
settingsViewModel.firstDayOfWeek,
)}
</div>
);
<hr></hr>
<h3>{coreViewModel.translations.settings.firstDayOfWeekLabel}</h3>
{OptionButtonList(
new React.ListState<[string, string]>(
coreViewModel.translations.regional.weekdays.full.map(
(x, i) => [x, i.toString()],
),
),
settingsViewModel.firstDayOfWeek,
)}
</div>
);
}
function SettingsAppearancePane(
@@ -183,34 +178,34 @@ function SettingsAppearancePane(
settingsViewModel: SettingsViewModel,
) {
return (
<div class="slide-up">
<h2>{coreViewModel.translations.settings.pages.appearance}</h2>
<hr></hr>
{OptionButtonList(
new React.ListState<[string, string]>([
[
coreViewModel.translations.settings.themes.dynamic,
ThemeSettings.Dynamic,
],
[
coreViewModel.translations.settings.themes.system,
ThemeSettings.System,
],
[
coreViewModel.translations.settings.themes.dark,
ThemeSettings.Dark,
],
[
coreViewModel.translations.settings.themes.light,
ThemeSettings.Light,
],
[
coreViewModel.translations.settings.themes.black,
ThemeSettings.Black,
],
]),
settingsViewModel.theme,
)}
</div>
);
<div class="slide-up">
<h2>{coreViewModel.translations.settings.pages.appearance}</h2>
<hr></hr>
{OptionButtonList(
new React.ListState<[string, string]>([
[
coreViewModel.translations.settings.themes.dynamic,
ThemeSettings.Dynamic,
],
[
coreViewModel.translations.settings.themes.system,
ThemeSettings.System,
],
[
coreViewModel.translations.settings.themes.dark,
ThemeSettings.Dark,
],
[
coreViewModel.translations.settings.themes.light,
ThemeSettings.Light,
],
[
coreViewModel.translations.settings.themes.black,
ThemeSettings.Black,
],
]),
settingsViewModel.theme,
)}
</div>
);
}
+81 -70
View File
@@ -1,94 +1,105 @@
import { SplitModal } from "./splitModal";
import { UsageBar } from "../Components/usageBar";
import { DirectoryItemList } from "../Components/directoryItemList";
import { DangerousActionButton } from "../Components/dangerousActionButton";
import * as React from "../../react";
import StorageViewModel from "../../ViewModel/Global/storageViewModel";
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import { PATH_COMPONENT_SEPARATOR } from "../../Model/Global/storageModel";
import {UsageBar} from "../Components/usageBar";
export function StorageModal(
coreViewModel: CoreViewModel,
storageViewModel: StorageViewModel,
) {
const usageValueLabel = React.createProxyState([storageViewModel.occupiedSpaceMB, storageViewModel.maximumSpaceMB], ()=>
coreViewModel.translations.storage.usageVauleLabel(storageViewModel.occupiedSpaceMB.value, storageViewModel.maximumSpaceMB.value));
const usageValueLabel = React.createProxyState(
[storageViewModel.occupiedSpaceMB, storageViewModel.maximumSpaceMB],
() =>
coreViewModel.translations.storage.usageVauleLabel(
storageViewModel.occupiedSpaceMB.value,
storageViewModel.maximumSpaceMB.value,
),
);
const detailView = React.createProxyState(
[storageViewModel.selectedPath],
() => {
if (storageViewModel.selectedPath.value == PATH_COMPONENT_SEPARATOR)
return (
<div class="flex-column gap">
<span class="secondary">
{coreViewModel.translations.storage.noItemSelected}
</span>
[storageViewModel.selectedPath],
() => {
if (storageViewModel.selectedPath.value == PATH_COMPONENT_SEPARATOR)
return (
<div class="flex-column gap">
<span class="secondary">
{coreViewModel.translations.storage.noItemSelected}
</span>
<hr></hr>
<hr></hr>
{UsageBar(coreViewModel.translations.storage.usageLabel, usageValueLabel, storageViewModel.occupiedSpaceMB, storageViewModel.maximumSpaceMB)}
{UsageBar(
coreViewModel.translations.storage.usageLabel,
usageValueLabel,
storageViewModel.occupiedSpaceMB,
storageViewModel.maximumSpaceMB,
)}
<hr></hr>
<hr></hr>
{DangerousActionButton(
coreViewModel,
coreViewModel.translations.storage.removeJunkButton,
"delete_forever",
storageViewModel.removeJunk,
)}
</div>
);
{DangerousActionButton(
coreViewModel,
coreViewModel.translations.storage.removeJunkButton,
"delete_forever",
storageViewModel.removeJunk,
)}
</div>
);
return (
<div class="flex-column gap">
<div class="tile flex-no">
<div>
<b>{coreViewModel.translations.storage.path}</b>
<span
class="break-all"
subscribe:innerText={
storageViewModel.selectedPath
}
></span>
</div>
</div>
<div class="tile flex-no">
<div>
<b>{coreViewModel.translations.storage.content}</b>
<code
subscribe:innerText={
storageViewModel.selectedFileContent
}
></code>
</div>
</div>
return (
<div class="flex-column gap">
<div class="tile flex-no">
<div>
<b>{coreViewModel.translations.storage.path}</b>
<span
class="break-all"
subscribe:innerText={
storageViewModel.selectedPath
}
></span>
</div>
</div>
<div class="tile flex-no">
<div>
<b>{coreViewModel.translations.storage.content}</b>
<code
subscribe:innerText={
storageViewModel.selectedFileContent
}
></code>
</div>
</div>
{DangerousActionButton(
coreViewModel,
coreViewModel.translations.storage.deleteItem,
"delete_forever",
storageViewModel.deleteSelectedItem,
)}
</div>
);
},
{DangerousActionButton(
coreViewModel,
coreViewModel.translations.storage.deleteItem,
"delete_forever",
storageViewModel.deleteSelectedItem,
)}
</div>
);
},
);
return (
<div class="modal" toggle:open={storageViewModel.isShowingStorageModal}>
<div>
<main class="padding-0">
{SplitModal(
coreViewModel,
new React.State(DirectoryItemList(storageViewModel)),
detailView,
true,
)}
</main>
<button on:click={storageViewModel.close}>
{coreViewModel.translations.general.closeButton}
<span class="icon">close</span>
</button>
</div>
</div>
<div class="modal" toggle:open={storageViewModel.isShowingStorageModal}>
<div>
<main class="padding-0">
{SplitModal(
coreViewModel,
new React.State(DirectoryItemList(storageViewModel)),
detailView,
true,
)}
</main>
<button on:click={storageViewModel.close}>
{coreViewModel.translations.general.closeButton}
<span class="icon">close</span>
</button>
</div>
</div>
);
}