Added notification system
mellobacon committed
Nov 29, 2022 at 17:00 UTC
656966e725b51ea4a1b18ac221bcaf72c002bd8c
5 files changed
+303
-3
src/components/Editor/CodeMirrorEditor.svelte
+10
@@ -7,6 +7,10 @@
7
import { default_theme } from "./scripts/DefaultTheme";
8
import { file_language, file_linefeed, line_info } from "./scripts/Editor";
9
import { fs } from "@tauri-apps/api";
10
+ import settings from "../../config/nucleus-settings.json";
11
+ import { addNotification, NotifType } from "../Notifications/Notifications";
12
+
13
+ const shortcuts = Object.entries(settings.shortcuts)
14
15
export let hidden = false;
16
let editorElement;
@@ -67,6 +71,7 @@
71
clearTimeout(_);
72
_ = setTimeout(() => {
73
if (!file_info || !file_info.path) {
74
+ addNotification(NotifType.Message, "File path not found, cannot save.");
75
console.log("File path not found, cannot save.");
76
}
77
else if (file_info.path !== "") {
@@ -90,6 +95,11 @@ on:input={async (e) => {
95
await updateDom();
96
getLineInfo();
97
}}
98
+on:keydown={(e) => {
99
+ const code = e.code;
100
+ const key = e.key.charAt(0).toUpperCase() + e.key.slice(1);
101
+ //TODO: Figure out shortcuts
102
+}}
103
on:keydown={async (e) => {
104
let key = e.code;
105
switch(key) {
src/components/Footer/Footer.svelte
+18
-3
@@ -7,6 +7,8 @@
7
import LanguageList from "./LanguageList.svelte";
8
import { languages } from "@codemirror/language-data";
9
import { file_language, file_linefeed, line_info } from "../Editor/scripts/Editor";
10
+ import Notifications from "../Notifications/Notifications.svelte";
11
+ import { unreadnotifications } from "../Notifications/Notifications";
12
13
let langs = [];
14
for (let l of languages) {
@@ -14,9 +16,8 @@
16
langs = [...langs, langdata];
17
}
18
let showlangs = false;
17
-
19
const tools = [
19
- {name: "Notifications", content: "notifications"}
20
+ {name: "Notifications", content: Notifications}
21
]
22
23
async function spawnTerminal() {
@@ -56,9 +57,10 @@
57
<span class="toolname">Terminal</span>
58
</span>
59
{#each tools as tool}
59
- <span class="tool">
60
+ <span class="tool" class:updated={$unreadnotifications}>
61
<!-- svelte-ignore a11y-click-events-have-key-events -->
62
<span class="toolname" on:click={() => {togglePanel(tool)}}>{tool.name}</span>
63
+ <span class="notification"></span>
64
</span>
65
{/each}
66
</div>
@@ -122,6 +124,19 @@
124
.tool {
125
height: 100%;
126
padding: 0 5px;
127
+ position: relative;
128
+ &.updated {
129
+ .notification {
130
+ position: absolute;
131
+ width: 8px;
132
+ height: 8px;
133
+ display: block;
134
+ background-color: #4589ff;
135
+ border-radius: 50%;
136
+ top: 5px;
137
+ left: 5px;
138
+ }
139
+ }
140
}
141
.toolname {
142
margin-left: 5px;
src/components/Notifications/Notification.svelte
new
+139
@@ -0,0 +1,139 @@
1
+<script lang="ts">
2
+ import { updateReadStatus } from "./Notifications";
3
+
4
+ export let id;
5
+ export let type = "";
6
+ export let message = "";
7
+ export let read = false;
8
+ export let actions = [];
9
+
10
+ let notification;
11
+</script>
12
+<div class="info">
13
+ <span>01:25:47 PM CST</span>
14
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
15
+ <span class="mark-read" on:click|preventDefault={() => {
16
+ read = !read
17
+ updateReadStatus(id, read);
18
+ }
19
+ }>
20
+ {#if read}
21
+ Mark as unread
22
+ {:else}
23
+ Mark as read
24
+ {/if}
25
+ </span>
26
+</div>
27
+<div bind:this={notification} class="notification" class:error={type === "Error"} class:warning={type === "Warning"} class:read>
28
+ <div class="details">
29
+ <div class="icon"></div>
30
+ <div class="message">{type}: {message}</div>
31
+ </div>
32
+ <div class="actions">
33
+ {#each actions as action}
34
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
35
+ <div class="action-button" on:click={action.action}>{action.label}</div>
36
+ {/each}
37
+ </div>
38
+</div>
39
+
40
+<style lang="scss">
41
+ $notification-height: 3rem;
42
+ $notification-border-width: 1px;
43
+ $error-color: #c02b2b;
44
+ $warning-color: #ffeb10;
45
+ $default-color: #4589ff;
46
+ .info {
47
+ color: #6d6d6d;
48
+ font-weight: 200;
49
+ font-size: 0.7rem;
50
+ margin-bottom: 0.5rem;
51
+ margin-top: 0.8rem;
52
+ display: flex;
53
+ align-items: center;
54
+ span {
55
+ margin: 0 5px;
56
+ }
57
+ .mark-read {
58
+ text-decoration: underline;
59
+ cursor: pointer;
60
+ &:hover {
61
+ color: #939393;
62
+ }
63
+ }
64
+ }
65
+ .notification {
66
+ display: flex;
67
+ min-height: $notification-height;
68
+ background-color: #1f1f1f;
69
+ margin-bottom: 0.8rem;
70
+ position: relative;
71
+ border-top: 3px solid $default-color;
72
+ &.warning {
73
+ border-top: 3px solid $warning-color;
74
+ .icon {
75
+ background-color: $warning-color !important;
76
+ }
77
+ }
78
+ &.error {
79
+ border-top: 3px solid $error-color;
80
+ .icon {
81
+ background-color: $error-color !important;
82
+ }
83
+ }
84
+ &.read {
85
+ border-top-color: #333;
86
+ color: #838383;
87
+ .icon {
88
+ background-color: #333 !important;
89
+ }
90
+ .action-button {
91
+ color: #bdd4ff75 !important;
92
+ }
93
+ }
94
+ &::before {
95
+ position: absolute;
96
+ height: 100%;
97
+ width: 100%;
98
+ border-width: 0 $notification-border-width $notification-border-width $notification-border-width;
99
+ border-style: solid;
100
+ box-sizing: border-box;
101
+ border-color: #333;
102
+ content: "";
103
+ }
104
+ .details {
105
+ display: flex;
106
+ margin: 0 0.5rem;
107
+ position: relative;
108
+ top: -1px;
109
+ align-items: center;
110
+ .icon {
111
+ border-radius: 50%;
112
+ background-color: $default-color;
113
+ width: 20px;
114
+ height: 20px;
115
+ margin-right: 10px;
116
+ }
117
+ }
118
+ .actions {
119
+ display: flex;
120
+ margin: 0 0.5rem;
121
+ position: relative;
122
+ top: -1px;
123
+ align-items: center;
124
+ .action-button {
125
+ display: flex;
126
+ justify-content: center;
127
+ align-items: center;
128
+ color: #BDD4FF;
129
+ cursor: pointer;
130
+ &:nth-child(2) {
131
+ margin: 0 5px;
132
+ }
133
+ &:hover {
134
+ text-decoration: underline;
135
+ }
136
+ }
137
+ }
138
+ }
139
+</style>
\ No newline at end of file
src/components/Notifications/Notifications.svelte
new
+43
@@ -0,0 +1,43 @@
1
+<script lang="ts">
2
+ import { clearNotifications, notifications } from "./Notifications";
3
+ import Notification from "./Notification.svelte";
4
+</script>
5
+
6
+<!-- svelte-ignore a11y-click-events-have-key-events -->
7
+<div id="title">Notifications <span on:click={() => {clearNotifications()}}>Clear Notifications</span></div>
8
+<div id="notifications">
9
+ {#each $notifications as notification}
10
+ <Notification id={notification.id} type={notification.type} message={notification.message} read={notification.read} actions={notification.actions}></Notification>
11
+ {/each}
12
+</div>
13
+
14
+<style lang="scss">
15
+ #notifications {
16
+ width: 100%;
17
+ height: 100%;
18
+ padding: 5px;
19
+ margin-top: 2rem;
20
+ }
21
+ #title {
22
+ min-height: 2rem;
23
+ display: flex;
24
+ align-items: center;
25
+ border-bottom: 1px solid #333;
26
+ padding: 5px;
27
+ position: absolute;
28
+ width: -webkit-fill-available;
29
+ background-color: #161616;
30
+ z-index: 1;
31
+ justify-content: space-between;
32
+ box-shadow: 0px 3px 3px -2px #161616;
33
+ span {
34
+ font-weight: 200;
35
+ font-size: 0.7rem;
36
+ color: #BDD4FF;
37
+ cursor: pointer;
38
+ &:hover {
39
+ text-decoration: underline;
40
+ }
41
+ }
42
+ }
43
+</style>
\ No newline at end of file
src/components/Notifications/Notifications.ts
new
+93
@@ -0,0 +1,93 @@
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 let unreadnotifications = writable(false);
44
+export const notifications = writable([]);
45
+let notificationlist = [];
46
+
47
+export function addNotification(type, message, 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
+export function updateReadStatus(id, read) {
56
+ for (const notification of notificationlist) {
57
+ if (notification.id === id) {
58
+ notification.read = read;
59
+ break;
60
+ }
61
+ }
62
+ updateNotificationStatus();
63
+}
64
+export function clearNotifications() {
65
+ notificationlist = [];
66
+ notifications.set(notificationlist);
67
+ updateNotificationStatus();
68
+}
69
+function getType(type: NotifType) {
70
+ switch (type) {
71
+ case NotifType.Message:
72
+ return "Message";
73
+ case NotifType.Warning:
74
+ return "Warning";
75
+ case NotifType.Error:
76
+ return "Error";
77
+ default:
78
+ return "Message";
79
+ }
80
+}
81
+function updateNotificationStatus() {
82
+ let unread = false;
83
+ for (const notification of notificationlist) {
84
+ if (!notification.read) {
85
+ unread = true;
86
+ break;
87
+ }
88
+ else {
89
+ unread = false;
90
+ }
91
+ }
92
+ unreadnotifications.set(unread);
93
+}
\ No newline at end of file