chore: get time function
mellbacon committed
May 21, 2024 at 13:10 UTC
dc9a2a846843769e12e1cddf7bbf7e0280633a64
5 files changed
+313
-11
src/config/config.ts
+5
@@ -119,3 +119,8 @@ export async function openLogFiles() {
119
let recent_log = (await fs.readDir(`${log_dir}logs`)).at(-1);
120
await invoke("open_in_explorer", {path: `${log_dir}logs${path.sep}${recent_log.name}`});
121
}
122
+
123
+export function getTime() {
124
+ let time = new Intl.DateTimeFormat('en-US', {dateStyle: "short", timeStyle: "long"}).format();
125
+ return time;
126
+}
src/lib/Notifications/Notification.svelte
new
+162
@@ -0,0 +1,162 @@
1
+<script lang="ts">
2
+ import { getTime } from "../../config/config";
3
+ import { updateReadStatus } from "./notifications";
4
+
5
+ export let id;
6
+ export let type = "";
7
+ export let message = "";
8
+ export let read = false;
9
+ export let actions = [];
10
+
11
+ let notification: HTMLElement | null;
12
+</script>
13
+
14
+<div class="info">
15
+ <span>{getTime()}</span>
16
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
17
+ <span class="mark-read" on:click|preventDefault={() => {
18
+ read = !read
19
+ updateReadStatus(id, read);
20
+ }
21
+ }>
22
+ {#if read}
23
+ Mark as unread
24
+ {:else}
25
+ Mark as read
26
+ {/if}
27
+ </span>
28
+</div>
29
+<div bind:this={notification} class="notification" class:error={type === "Error"} class:warning={type === "Warning"} class:read>
30
+ <div class="details">
31
+ <div class="icon"></div>
32
+ <div class="message">{type}: {message}</div>
33
+ </div>
34
+ <div class="actions">
35
+ {#each actions as action}
36
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
37
+ <div class="action-button" on:click={action.action}>{action.label}</div>
38
+ {/each}
39
+ </div>
40
+
41
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
42
+ <span class="close-button" on:click={() => {}}></span>
43
+</div>
44
+
45
+<style lang="scss">
46
+ $notification-height: 3rem;
47
+ $notification-border-width: 1px;
48
+ $error-color: #c02b2b;
49
+ $warning-color: #ffeb10;
50
+ $default-color: #4589ff;
51
+ .info {
52
+ color: #6d6d6d;
53
+ font-weight: 200;
54
+ font-size: 0.7rem;
55
+ margin-bottom: 0.5rem;
56
+ margin-top: 0.2rem;
57
+ display: flex;
58
+ align-items: center;
59
+ span {
60
+ margin: 0 5px;
61
+ }
62
+ .mark-read {
63
+ text-decoration: underline;
64
+ cursor: pointer;
65
+ &:hover {
66
+ color: #939393;
67
+ }
68
+ }
69
+ }
70
+ .notification {
71
+ display: flex;
72
+ min-height: $notification-height;
73
+ background-color: #1f1f1f;
74
+ margin-bottom: 0.8rem;
75
+ position: relative;
76
+ border-top: 3px solid $default-color;
77
+ justify-content: space-between;
78
+ &.warning {
79
+ border-top: 3px solid $warning-color;
80
+ .icon {
81
+ background-color: $warning-color !important;
82
+ }
83
+ }
84
+ &.error {
85
+ border-top: 3px solid $error-color;
86
+ .icon {
87
+ background-color: $error-color !important;
88
+ }
89
+ }
90
+ &.read {
91
+ border-top-color: #333;
92
+ color: #838383;
93
+ .icon {
94
+ background-color: #333 !important;
95
+ }
96
+ .action-button {
97
+ color: #bdd4ff75 !important;
98
+ }
99
+ :global(.bx--toast-notification__close-button .bx--toast-notification__close-icon) {
100
+ fill: #bdd4ff75 !important;
101
+ }
102
+ }
103
+ &::before {
104
+ position: absolute;
105
+ height: 100%;
106
+ width: 100%;
107
+ border-width: 0 $notification-border-width $notification-border-width $notification-border-width;
108
+ border-style: solid;
109
+ box-sizing: border-box;
110
+ border-color: #333;
111
+ content: "";
112
+ }
113
+ .details {
114
+ display: flex;
115
+ margin: 0 0.5rem;
116
+ position: relative;
117
+ top: -1px;
118
+ align-items: center;
119
+ .icon {
120
+ border-radius: 50%;
121
+ background-color: $default-color;
122
+ min-width: 20px;
123
+ min-height: 20px;
124
+ margin-right: 10px;
125
+ }
126
+ }
127
+ .actions {
128
+ display: flex;
129
+ margin: 0 0.5rem;
130
+ position: relative;
131
+ top: -1px;
132
+ align-items: center;
133
+ .action-button {
134
+ display: flex;
135
+ justify-content: center;
136
+ align-items: center;
137
+ color: #BDD4FF;
138
+ cursor: pointer;
139
+ &:nth-child(2) {
140
+ margin: 0 0.6rem;
141
+ }
142
+ &:hover {
143
+ text-decoration: underline;
144
+ }
145
+ }
146
+ }
147
+ .close-button {
148
+ font-size: 12px;
149
+ padding: 0 7px;
150
+ display: flex;
151
+ align-items: center;
152
+ justify-content: center;
153
+ border-radius: 3px;
154
+ margin: 0 0.5rem;
155
+ cursor: pointer;
156
+ &::before {
157
+ content: "\2715";
158
+ }
159
+ z-index: 10;
160
+ }
161
+ }
162
+</style>
\ No newline at end of file
src/lib/Notifications/NotificationList.svelte
new
+18
@@ -0,0 +1,18 @@
1
+<script lang="ts">
2
+ import { notifications } from "./notifications";
3
+ import Notification from "./Notification.svelte";
4
+</script>
5
+
6
+<div id="notifications">
7
+{#each $notifications as notification}
8
+ <Notification id={notification.id} type={notification.type} message={notification.message} read={notification.read} actions={notification.actions}></Notification>
9
+{/each}
10
+</div>
11
+
12
+<style lang="scss">
13
+#notifications {
14
+ height: calc(100% - 1.5rem);
15
+ padding: 5px;
16
+ overflow: auto;
17
+}
18
+</style>
\ No newline at end of file
src/lib/Notifications/notifications.ts
new
+100
@@ -0,0 +1,100 @@
1
+import { writable } from "svelte/store";
2
+
3
+export enum NotifType {
4
+ Message,
5
+ Warning,
6
+ Error
7
+}
8
+class Notification {
9
+ id;
10
+ type: string;
11
+ message: string;
12
+ read: boolean;
13
+ actions;
14
+ constructor(message, type, read = false, actions = []) {
15
+ this.message = message;
16
+ this.type = getType(type);
17
+ this.read = read;
18
+ this.actions = actions;
19
+ }
20
+}
21
+export class Action {
22
+ label: string;
23
+ action: () => void | null;
24
+ constructor(label, action: () => void | null = null) {
25
+ this.label = label;
26
+ this.action = action;
27
+ }
28
+}
29
+
30
+const actions = [
31
+ new Action(" Test Message", () => {
32
+ addNotification(NotifType.Message, "Message notification success");
33
+ }),
34
+ new Action("Test Warning", () => {
35
+ addNotification(NotifType.Warning, "Warning notification success");
36
+ }),
37
+ new Action("Test Error", () => {
38
+ addNotification(NotifType.Error, "Failure or something idk");
39
+ })
40
+]
41
+
42
+let id = 0;
43
+export const unreadnotifications = writable(false);
44
+export const notifications = writable([]);
45
+let notificationlist = [];
46
+
47
+export function addNotification(type: NotifType, message: string, actions = []) {
48
+ let notification = new Notification(message, type, false, actions);
49
+ notification.id = id;
50
+ notificationlist = [...notificationlist, notification];
51
+ notifications.set(notificationlist);
52
+ updateNotificationStatus();
53
+ id++;
54
+}
55
+
56
+export function updateReadStatus(id, read) {
57
+ for (const notification of notificationlist) {
58
+ if (notification.id === id) {
59
+ notification.read = read;
60
+ break;
61
+ }
62
+ }
63
+ updateNotificationStatus();
64
+}
65
+
66
+export function clearNotifications() {
67
+ notificationlist = [];
68
+ notifications.set(notificationlist);
69
+ updateNotificationStatus();
70
+}
71
+
72
+export function closeNotification(id) {
73
+ notificationlist = notificationlist.filter(n => n.id !== id);
74
+ notifications.set(notificationlist);
75
+ updateNotificationStatus();
76
+}
77
+
78
+function getType(type: NotifType) {
79
+ switch (type) {
80
+ case NotifType.Message:
81
+ return "Message";
82
+ case NotifType.Warning:
83
+ return "Warning";
84
+ case NotifType.Error:
85
+ return "Error";
86
+ default:
87
+ return "Message";
88
+ }
89
+}
90
+
91
+function updateNotificationStatus() {
92
+ let unread = false;
93
+ for (const notification of notificationlist) {
94
+ if (!notification.read) {
95
+ unread = true;
96
+ break;
97
+ }
98
+ }
99
+ unreadnotifications.set(unread);
100
+}
src/lib/ToolView.svelte
+28
-11
@@ -1,23 +1,26 @@
1
<script lang="ts">
2
- import { closeBottomPanel, hideBottomPanel } from "./Statusbar.svelte";
2
+ import { hideBottomPanel } from "./Statusbar.svelte";
3
import Ellipsis from "carbon-icons-svelte/lib/OverflowMenuHorizontal.svelte";
4
import Menu from "./utility/Menu.svelte";
5
- import { clearTerminal, closeTerminal } from "./Terminal.svelte";
5
6
export let content = null;
7
export let name = "Tool";
8
+ export let options = [];
9
+ export let buttons;
10
</script>
11
<div id="toolview">
12
<div class="header">{name}
13
<div class="header-tools">
14
<div class="tool-buttons">
14
- <Menu right menu={{icon: Ellipsis, children: [
15
- {name: "Clear Terminal", action: () => {
16
- clearTerminal();
17
- }}, {name: "Close Terminal", action: () => {
18
- closeBottomPanel();
19
- closeTerminal();
20
- }}]}}></Menu>
15
+ {#each buttons as button}
16
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
17
+ <div class="tool-button" title={button.title} on:click={button.action}>
18
+ <svelte:component this={button.icon}></svelte:component>
19
+ </div>
20
+ {/each}
21
+ {#if options}
22
+ <Menu right menu={{icon: Ellipsis, children: options}}></Menu>
23
+ {/if}
24
</div>
25
<!-- svelte-ignore a11y-click-events-have-key-events -->
26
<span class="close" title="Hide Panel" on:click={hideBottomPanel}></span>
@@ -36,7 +39,6 @@
39
.toolview-container {
40
padding: 8px;
41
height: 100%;
39
-
42
}
43
.header {
44
height: 35px;
@@ -53,12 +55,27 @@
55
flex-direction: row;
56
.tool-buttons {
57
height: 24px;
58
+ display: flex;
59
+ flex-direction: row;
60
+ align-items: center;
61
+ justify-content: center;
62
}
57
- :global(.menu-button) {
63
+ .tool-button {
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: center;
67
+ height: 100%;
68
+ cursor: pointer;
69
+ }
70
+ :global(.menu-button), .tool-button {
71
min-width: unset;
72
padding: 0 7px;
73
border-radius: 3px;
74
}
75
+ :global(.tool-button svg) {
76
+ width: 18px;
77
+ height: 18px;
78
+ }
79
}
80
.close {
81
font-size: 12px;