@setoelkahfi / svara / commits / 656966e

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