add colors

This commit is contained in:
marlon-erler
2024-07-20 18:10:42 +02:00
parent e3c902f60e
commit 3e570afd05
11 changed files with 287 additions and 28 deletions
+50
View File
@@ -89,6 +89,56 @@ main {
} }
} }
/* src/colors.css */
[color]:not([color=standard]) {
--primary: hsl(var(--hue), 100%, 80%);
--gradient-luma-1: 70%;
--gradient-luma-2: 90%;
--hover-luma: 75%;
--active-luma: 80%;
--text-luma: 20%;
--surface:
linear-gradient(
120deg,
hsl(var(--hue), 100%, var(--gradient-luma-1)),
hsl(var(--hue), 100%, var(--gradient-luma-2)) );
--surface-hover: hsl(var(--hue), 100%, var(--hover-luma));
--surface-active: hsl(var(--hue), 100%, var(--active-luma));
--text-normal: hsl(var(--hue), 100%, var(--text-luma));
}
@media (prefers-color-scheme: dark) {
[color]:not([color=standard]) {
--gradient-luma-1: 20%;
--gradient-luma-2: 15%;
--hover-luma: 25%;
--active-luma: 30%;
--text-luma: 90%;
}
}
[color]:not([color=standard]).fill-color {
color: white;
background-color: var(--primary);
}
[color=coral] {
--hue: 20;
}
[color=yellow] {
--hue: 50;
}
[color=mint] {
--hue: 150;
}
[color=lightblue] {
--hue: 180;
}
[color=blue] {
--hue: 220;
}
[color=purple] {
--hue: 280;
}
/* src/View/chatPage.css */ /* src/View/chatPage.css */
#chat-page-wrapper { #chat-page-wrapper {
display: none; display: none;
+66 -2
View File
@@ -353,11 +353,24 @@
chats: [DATA_VERSION, "chat"], chats: [DATA_VERSION, "chat"],
chatInfo: (id) => [DATA_VERSION, "chat", id, "info"], chatInfo: (id) => [DATA_VERSION, "chat", id, "info"],
chatLastUsedPage: (id) => [DATA_VERSION, "chat", id, "last-used-page"], chatLastUsedPage: (id) => [DATA_VERSION, "chat", id, "last-used-page"],
chatColor: (id) => [DATA_VERSION, "chat", id, "color"],
chatMessages: (id) => [DATA_VERSION, "chat", id, "messages"], chatMessages: (id) => [DATA_VERSION, "chat", id, "messages"],
chatObjects: (id) => [DATA_VERSION, "chat", id, "objects"], chatObjects: (id) => [DATA_VERSION, "chat", id, "objects"],
chatOutbox: (id) => [DATA_VERSION, "chat", id, "outbox"] chatOutbox: (id) => [DATA_VERSION, "chat", id, "outbox"]
}; };
// src/ViewModel/colors.ts
var Color = /* @__PURE__ */ ((Color2) => {
Color2["Standard"] = "standard";
Color2["Coral"] = "coral";
Color2["Yellow"] = "yellow";
Color2["Mint"] = "mint";
Color2["LightBlue"] = "lightblue";
Color2["Blue"] = "blue";
Color2["purple"] = "purple";
return Color2;
})(Color || {});
// src/Model/chatModel.ts // src/Model/chatModel.ts
var ChatModel = class { var ChatModel = class {
// data // data
@@ -365,6 +378,7 @@
storageModel; storageModel;
chatListModel; chatListModel;
info; info;
color;
messages = /* @__PURE__ */ new Set(); messages = /* @__PURE__ */ new Set();
objects = /* @__PURE__ */ new Map(); objects = /* @__PURE__ */ new Map();
outbox = /* @__PURE__ */ new Set(); outbox = /* @__PURE__ */ new Set();
@@ -372,6 +386,9 @@
get infoPath() { get infoPath() {
return storageKeys.chatInfo(this.id); return storageKeys.chatInfo(this.id);
} }
get colorPath() {
return storageKeys.chatColor(this.id);
}
get messageDirPath() { get messageDirPath() {
return storageKeys.chatMessages(this.id); return storageKeys.chatMessages(this.id);
} }
@@ -397,10 +414,17 @@
this.info.secondaryChannels = secondaryChannels; this.info.secondaryChannels = secondaryChannels;
this.storeInfo(); this.storeInfo();
}; };
setColor = (color) => {
this.color = color;
this.storeColor();
};
// store & add // store & add
storeInfo = () => { storeInfo = () => {
this.storageModel.storeStringifiable(this.infoPath, this.info); this.storageModel.storeStringifiable(this.infoPath, this.info);
}; };
storeColor = () => {
this.storageModel.store(this.colorPath, this.color);
};
addMessage = (message) => { addMessage = (message) => {
if (!this.messages.has(message)) { if (!this.messages.has(message)) {
this.messages.add(message); this.messages.add(message);
@@ -429,6 +453,15 @@
this.info = generateChatInfo("0"); this.info = generateChatInfo("0");
} }
}; };
restoreColor = () => {
const path = this.colorPath;
const color = this.storageModel.restore(path);
if (!color) {
this.color = "standard" /* Standard */;
} else {
this.color = color;
}
};
restoreMessages = () => { restoreMessages = () => {
const messageIds = this.storageModel.list(this.messageDirPath); const messageIds = this.storageModel.list(this.messageDirPath);
if (!Array.isArray(messageIds)) return; if (!Array.isArray(messageIds)) return;
@@ -465,6 +498,7 @@
this.storageModel = storageModel2; this.storageModel = storageModel2;
this.chatListModel = chatListModel; this.chatListModel = chatListModel;
this.restoreInfo(); this.restoreInfo();
this.restoreColor();
} }
}; };
function generateChatInfo(primaryChannel) { function generateChatInfo(primaryChannel) {
@@ -532,6 +566,7 @@
index = new State(0); index = new State(0);
primaryChannel = new State(""); primaryChannel = new State("");
primaryChannelInput = new State(""); primaryChannelInput = new State("");
color = new State("standard" /* Standard */);
selectedPage = new State( selectedPage = new State(
"messages" /* Messages */ "messages" /* Messages */
); );
@@ -557,6 +592,10 @@
this.primaryChannel.value = this.chatModel.info.primaryChannel; this.primaryChannel.value = this.chatModel.info.primaryChannel;
this.chatListViewModel.updateIndices(); this.chatListViewModel.updateIndices();
}; };
setColor = (newColor) => {
this.color.value = newColor;
this.chatModel.setColor(newColor);
};
// restore // restore
restorePageSelection = () => { restorePageSelection = () => {
const path = storageKeys.chatLastUsedPage(this.chatModel.id); const path = storageKeys.chatLastUsedPage(this.chatModel.id);
@@ -575,6 +614,7 @@
this.chatListViewModel = chatListViewModel2; this.chatListViewModel = chatListViewModel2;
this.primaryChannel.value = chatModel.info.primaryChannel; this.primaryChannel.value = chatModel.info.primaryChannel;
this.primaryChannelInput.value = chatModel.info.primaryChannel; this.primaryChannelInput.value = chatModel.info.primaryChannel;
this.color.value = chatModel.color;
this.restorePageSelection(); this.restorePageSelection();
this.updateIndex(); this.updateIndex();
} }
@@ -760,7 +800,31 @@
}, },
translations.general.setButton, translations.general.setButton,
/* @__PURE__ */ createElement("span", { class: "icon" }, "check") /* @__PURE__ */ createElement("span", { class: "icon" }, "check")
)))); )), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement(
"div",
{
class: "flex-row gap width-input"
},
...Object.values(Color).map((color) => {
const isSelected = createProxyState(
[chatViewModel.color],
() => chatViewModel.color.value == color
);
function setColor() {
chatViewModel.setColor(color);
}
return /* @__PURE__ */ createElement(
"button",
{
color,
class: "fill-color width-100 flex",
style: "height: 2rem",
"toggle:selected": isSelected,
"on:click": setColor
}
);
})
)));
} }
// src/View/chatPage.tsx // src/View/chatPage.tsx
@@ -1156,7 +1220,7 @@
// src/View/Components/chatEntry.tsx // src/View/Components/chatEntry.tsx
function ChatEntry(chatViewModel) { function ChatEntry(chatViewModel) {
const view = /* @__PURE__ */ createElement("button", { class: "chat-entry tile", "on:click": chatViewModel.open }, /* @__PURE__ */ createElement( const view = /* @__PURE__ */ createElement("button", { "set:color": chatViewModel.color, class: "chat-entry tile", "on:click": chatViewModel.open }, /* @__PURE__ */ createElement(
"span", "span",
{ {
class: "shadow", class: "shadow",
+25 -21
View File
@@ -93,7 +93,11 @@ COLORS
/* basic */ /* basic */
html { html {
background-color: var(--background); background: var(--background);
color: var(--text-normal);
}
.tile {
color: var(--text-normal); color: var(--text-normal);
} }
@@ -102,12 +106,12 @@ html {
} }
.modal > div { .modal > div {
background-color: var(--background); background: var(--background);
} }
menu, menu,
header { header {
background-color: var(--background-transparent); background: var(--background-transparent);
} }
* { * {
@@ -145,53 +149,53 @@ textarea:not(.tile *),
select:not(.tile *), select:not(.tile *),
.surface, .surface,
.tile { .tile {
background-color: var(--surface); background: var(--surface);
} }
@media (hover: hover) { @media (hover: hover) {
button:hover, button:hover,
select:hover, select:hover,
button.tile.ghost:hover { button.tile.ghost:hover {
background-color: var(--surface-hover); background: var(--surface-hover);
} }
[selected]:hover { [selected]:hover {
background-color: var(--surface); background: var(--surface);
} }
} }
button:active, button:active,
select:active, select:active,
button.tile.ghost:active, button.tile.ghost:active,
[selected] { [selected] {
background-color: var(--surface-active); background: var(--surface-active);
} }
[selected]:active { [selected]:active {
background-color: var(--surface-hover); background: var(--surface-hover);
} }
.tile *, .tile *,
header button { header button {
background-color: transparent; background: transparent;
} }
input:checked { input:checked {
background-color: var(--primary); background: var(--primary);
} }
/* primary button */ /* primary button */
button.primary { button.primary {
background-color: var(--primary); background: var(--primary);
color: var(--text-on-color); color: var(--text-on-color);
} }
@media (hover: hover) { @media (hover: hover) {
button.primary:hover { button.primary:hover {
background-color: var(--primary-hover); background: var(--primary-hover);
} }
} }
button.primary:active { button.primary:active {
background-color: var(--primary-active); background: var(--primary-active);
} }
/* ghost button */ /* ghost button */
button.ghost:not(.tile) { button.ghost:not(.tile) {
background-color: transparent; background: transparent;
} }
button.ghost { button.ghost {
color: var(--link); color: var(--link);
@@ -201,25 +205,25 @@ button.ghost {
} }
@media (hover: hover) { @media (hover: hover) {
button.ghost:hover { button.ghost:hover {
background-color: var(--background-hover); background: var(--background-hover);
} }
} }
button.ghost:active { button.ghost:active {
background-color: var(--background-active); background: var(--background-active);
} }
/* danger button */ /* danger button */
button.danger { button.danger {
background-color: var(--danger); background: var(--danger);
color: var(--text-on-color); color: var(--text-on-color);
} }
@media (hover: hover) { @media (hover: hover) {
button.danger:hover { button.danger:hover {
background-color: var(--danger-hover); background: var(--danger-hover);
} }
} }
button.danger:active { button.danger:active {
background-color: var(--danger-active); background: var(--danger-active);
} }
/* /*
@@ -909,7 +913,7 @@ input[type="checkbox"]:checked::before {
input[type="radio"]:checked::before { input[type="radio"]:checked::before {
content: ""; content: "";
background-color: var(--text-on-color); background: var(--text-on-color);
border-radius: 100%; border-radius: 100%;
--inset: 4px; --inset: 4px;
@@ -929,7 +933,7 @@ input[type="radio"]:checked::before {
right: 0px; right: 0px;
bottom: 0px; bottom: 0px;
background-color: var(--background); background: var(--background);
opacity: 0.6; opacity: 0.6;
} }
+34 -4
View File
@@ -3,6 +3,7 @@
import StorageModel, { storageKeys } from "./storageModel"; import StorageModel, { storageKeys } from "./storageModel";
import ChatListModel from "./chatListModel"; import ChatListModel from "./chatListModel";
import { Color } from "../ViewModel/colors";
import { checkIsValidObject } from "./Utility/typeSafety"; import { checkIsValidObject } from "./Utility/typeSafety";
import { createTimestamp } from "./Utility/utility"; import { createTimestamp } from "./Utility/utility";
import { v4 } from "uuid"; import { v4 } from "uuid";
@@ -13,6 +14,7 @@ export class ChatModel {
storageModel: StorageModel; storageModel: StorageModel;
chatListModel: ChatListModel; chatListModel: ChatListModel;
info: ChatInfo; info: ChatInfo;
color: Color;
messages = new Set<ChatMessage>(); messages = new Set<ChatMessage>();
objects = new Map<string, ChatObject>(); objects = new Map<string, ChatObject>();
@@ -23,6 +25,10 @@ export class ChatModel {
return storageKeys.chatInfo(this.id); return storageKeys.chatInfo(this.id);
} }
get colorPath(): string[] {
return storageKeys.chatColor(this.id);
}
get messageDirPath(): string[] { get messageDirPath(): string[] {
return storageKeys.chatMessages(this.id); return storageKeys.chatMessages(this.id);
} }
@@ -55,11 +61,20 @@ export class ChatModel {
this.storeInfo(); this.storeInfo();
}; };
setColor = (color: Color): void => {
this.color = color;
this.storeColor();
};
// store & add // store & add
storeInfo = (): void => { storeInfo = (): void => {
this.storageModel.storeStringifiable(this.infoPath, this.info); this.storageModel.storeStringifiable(this.infoPath, this.info);
}; };
storeColor = (): void => {
this.storageModel.store(this.colorPath, this.color);
};
addMessage = (message: ChatMessage): void => { addMessage = (message: ChatMessage): void => {
if (!this.messages.has(message)) { if (!this.messages.has(message)) {
this.messages.add(message); this.messages.add(message);
@@ -81,7 +96,7 @@ export class ChatModel {
this.chatListModel.untrackChat(this); this.chatListModel.untrackChat(this);
// delete // delete
const dirPath = [...storageKeys.chats, this.id]; const dirPath: string[] = [...storageKeys.chats, this.id];
this.storageModel.removeRecursive(dirPath); this.storageModel.removeRecursive(dirPath);
}; };
@@ -89,9 +104,19 @@ export class ChatModel {
restoreInfo = (): void => { restoreInfo = (): void => {
const info: any = this.storageModel.restoreStringifiable(this.infoPath); const info: any = this.storageModel.restoreStringifiable(this.infoPath);
if (info != null) { if (info != null) {
this.info = info this.info = info;
} else { } else {
this.info = generateChatInfo("0") this.info = generateChatInfo("0");
}
};
restoreColor = (): void => {
const path: string[] = this.colorPath;
const color: string | null = this.storageModel.restore(path);
if (!color) {
this.color = Color.Standard;
} else {
this.color = color as any;
} }
}; };
@@ -135,12 +160,17 @@ export class ChatModel {
}; };
// init // init
constructor(storageModel: StorageModel, chatListModel: ChatListModel, chatId: string) { constructor(
storageModel: StorageModel,
chatListModel: ChatListModel,
chatId: string
) {
this.id = chatId; this.id = chatId;
this.storageModel = storageModel; this.storageModel = storageModel;
this.chatListModel = chatListModel; this.chatListModel = chatListModel;
this.restoreInfo(); this.restoreInfo();
this.restoreColor();
} }
} }
+1
View File
@@ -146,6 +146,7 @@ export const storageKeys = {
chats: [DATA_VERSION, "chat"], chats: [DATA_VERSION, "chat"],
chatInfo: (id: string) => [DATA_VERSION, "chat", id, "info"], chatInfo: (id: string) => [DATA_VERSION, "chat", id, "info"],
chatLastUsedPage: (id: string) => [DATA_VERSION, "chat", id, "last-used-page"], chatLastUsedPage: (id: string) => [DATA_VERSION, "chat", id, "last-used-page"],
chatColor: (id: string) => [DATA_VERSION, "chat", id, "color"],
chatMessages: (id: string) => [DATA_VERSION, "chat", id, "messages"], chatMessages: (id: string) => [DATA_VERSION, "chat", id, "messages"],
chatObjects: (id: string) => [DATA_VERSION, "chat", id, "objects"], chatObjects: (id: string) => [DATA_VERSION, "chat", id, "objects"],
chatOutbox: (id: string) => [DATA_VERSION, "chat", id, "outbox"], chatOutbox: (id: string) => [DATA_VERSION, "chat", id, "outbox"],
+28
View File
@@ -1,6 +1,7 @@
import * as React from "bloatless-react"; import * as React from "bloatless-react";
import ChatViewModel from "../../ViewModel/chatViewModel"; import ChatViewModel from "../../ViewModel/chatViewModel";
import { Color } from "../../ViewModel/colors";
import { translations } from "../translations"; import { translations } from "../translations";
export function SettingsPage(chatViewModel: ChatViewModel) { export function SettingsPage(chatViewModel: ChatViewModel) {
@@ -32,6 +33,33 @@ export function SettingsPage(chatViewModel: ChatViewModel) {
<span class="icon">check</span> <span class="icon">check</span>
</button> </button>
</div> </div>
<hr></hr>
<div
class="flex-row gap width-input"
>
{...Object.values(Color).map((color) => {
const isSelected = React.createProxyState(
[chatViewModel.color],
() => chatViewModel.color.value == color
);
function setColor() {
chatViewModel.setColor(color);
}
return (
<button
color={color}
class="fill-color width-100 flex"
style="height: 2rem"
toggle:selected={isSelected}
on:click={setColor}
></button>
);
})}
</div>
</div> </div>
</div> </div>
); );
+1 -1
View File
@@ -6,7 +6,7 @@ import ChatViewModel from "../../ViewModel/chatViewModel";
export function ChatEntry(chatViewModel: ChatViewModel) { export function ChatEntry(chatViewModel: ChatViewModel) {
const view = ( const view = (
<button class="chat-entry tile" on:click={chatViewModel.open}> <button set:color={chatViewModel.color} class="chat-entry tile" on:click={chatViewModel.open}>
<span <span
class="shadow" class="shadow"
subscribe:innerText={chatViewModel.primaryChannel} subscribe:innerText={chatViewModel.primaryChannel}
+10
View File
@@ -4,6 +4,7 @@ import StorageModel, { storageKeys } from "../Model/storageModel";
import ChatListViewModel from "./chatListViewModel"; import ChatListViewModel from "./chatListViewModel";
import { ChatModel } from "../Model/chatModel"; import { ChatModel } from "../Model/chatModel";
import { Color } from "./colors";
export default class ChatViewModel { export default class ChatViewModel {
chatModel: ChatModel; chatModel: ChatModel;
@@ -16,6 +17,8 @@ export default class ChatViewModel {
primaryChannel: React.State<string> = new React.State(""); primaryChannel: React.State<string> = new React.State("");
primaryChannelInput: React.State<string> = new React.State(""); primaryChannelInput: React.State<string> = new React.State("");
color: React.State<Color> = new React.State<Color>(Color.Standard);
selectedPage: React.State<ChatPageType> = new React.State<ChatPageType>( selectedPage: React.State<ChatPageType> = new React.State<ChatPageType>(
ChatPageType.Messages ChatPageType.Messages
); );
@@ -49,6 +52,11 @@ export default class ChatViewModel {
this.chatListViewModel.updateIndices(); this.chatListViewModel.updateIndices();
} }
setColor = (newColor: Color): void => {
this.color.value = newColor;
this.chatModel.setColor(newColor);
}
// restore // restore
restorePageSelection = (): void => { restorePageSelection = (): void => {
const path: string[] = storageKeys.chatLastUsedPage(this.chatModel.id); const path: string[] = storageKeys.chatLastUsedPage(this.chatModel.id);
@@ -71,6 +79,8 @@ export default class ChatViewModel {
this.primaryChannel.value = chatModel.info.primaryChannel; this.primaryChannel.value = chatModel.info.primaryChannel;
this.primaryChannelInput.value = chatModel.info.primaryChannel; this.primaryChannelInput.value = chatModel.info.primaryChannel;
this.color.value = chatModel.color;
this.restorePageSelection(); this.restorePageSelection();
this.updateIndex(); this.updateIndex();
} }
+9
View File
@@ -0,0 +1,9 @@
export enum Color {
Standard = "standard",
Coral = "coral",
Yellow = "yellow",
Mint = "mint",
LightBlue = "lightblue",
Blue = "blue",
purple = "purple",
}
+62
View File
@@ -0,0 +1,62 @@
[color]:not([color="standard"]) {
--primary: hsl(var(--hue), 100%, 80%);
--gradient-luma-1: 70%;
--gradient-luma-2: 90%;
--hover-luma: 75%;
--active-luma: 80%;
--text-luma: 20%;
--surface: linear-gradient(
120deg,
hsl(var(--hue), 100%, var(--gradient-luma-1)),
hsl(var(--hue), 100%, var(--gradient-luma-2))
);
--surface-hover: hsl(var(--hue), 100%, var(--hover-luma));
--surface-active: hsl(var(--hue), 100%, var(--active-luma));
--text-normal: hsl(var(--hue), 100%, var(--text-luma));
}
@media (prefers-color-scheme: dark) {
[color]:not([color="standard"]) {
--gradient-luma-1: 20%;
--gradient-luma-2: 15%;
--hover-luma: 25%;
--active-luma: 30%;
--text-luma: 90%;
}
}
[color]:not([color="standard"]).fill-color {
color: white;
background-color: var(--primary);
}
[color="coral"] {
--hue: 20;
}
[color="yellow"] {
--hue: 50;
}
[color="mint"] {
--hue: 150;
}
[color="lightblue"] {
--hue: 180;
}
[color="blue"] {
--hue: 220;
}
[color="purple"] {
--hue: 280;
}
+1
View File
@@ -1,4 +1,5 @@
import "./base.css"; import "./base.css";
import "./colors.css";
import * as React from "bloatless-react"; import * as React from "bloatless-react";