aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRémy Raes <contact@remyraes.com>2023-10-16 16:17:37 +0200
committerGitHub <noreply@github.com>2023-10-16 16:17:37 +0200
commit07ca1acee9a5f4227d18766b49b3fe6c8690e9a1 (patch)
tree824f2e659536dc9a5eb10f6e99bfb16da3d65647
parent4ddf57e0024dcd70a39473b15bbe1a0e1f69db88 (diff)
downloadFlightCore-07ca1acee9a5f4227d18766b49b3fe6c8690e9a1.tar.gz
FlightCore-07ca1acee9a5f4227d18766b49b3fe6c8690e9a1.zip
feat: Notification menu (#615)
Introduces a notification menu, which hosts notifications fired while the app was not focused. Notifications can be closed by the user.
-rw-r--r--src-vue/src/App.vue21
-rw-r--r--src-vue/src/components/NotificationButton.vue77
-rw-r--r--src-vue/src/i18n/lang/en.json5
-rw-r--r--src-vue/src/i18n/lang/fr.json5
-rw-r--r--src-vue/src/plugins/modules/notifications.ts31
-rw-r--r--src-vue/src/plugins/store.ts2
-rw-r--r--src-vue/src/style.css3
-rw-r--r--src-vue/src/utils/ui.ts6
8 files changed, 146 insertions, 4 deletions
diff --git a/src-vue/src/App.vue b/src-vue/src/App.vue
index 30e5e683..44e2c3e6 100644
--- a/src-vue/src/App.vue
+++ b/src-vue/src/App.vue
@@ -8,9 +8,11 @@ import { appWindow } from '@tauri-apps/api/window';
import { store } from './plugins/store';
import { Store } from 'tauri-plugin-store-api';
import { invoke } from "@tauri-apps/api";
+import NotificationButton from "./components/NotificationButton.vue";
export default {
components: {
+ NotificationButton,
ChangelogView,
DeveloperView,
PlayView,
@@ -73,6 +75,7 @@ export default {
<!-- Window controls -->
<div id="fc_window__controls">
+ <NotificationButton />
<el-button color="white" icon="SemiSelect" @click="minimize" circle />
<el-button color="white" icon="CloseBold" @click="close" circle />
</div>
@@ -119,7 +122,7 @@ export default {
height: 100%;
background-color: transparent;
float: left;
- width: calc(100% - 148px); /* window controls container width */
+ width: calc(100% - 168px); /* window controls container width */
}
#fc__menu_items .el-menu-item, #fc__menu_items .el-sub-menu__title {
@@ -167,7 +170,9 @@ export default {
height: 100%;
}
-#fc_window__controls > button {
+#fc_window__controls > button,
+#fc_window__controls > .el-dropdown > button,
+#fc_window__controls > .el-dropdown > .el-badge > button {
color: white;
font-size: 20px;
margin: auto 5px;
@@ -176,11 +181,14 @@ export default {
height: 100%;
}
-#fc_window__controls > button:hover {
+#fc_window__controls > button:hover,
+#fc_window__controls > .el-dropdown > button:hover,
+#fc_window__controls > .el-dropdown > .el-badge > button:hover {
color: #c6c9ce;
}
-#fc_window__controls > button:active {
+#fc_window__controls > button:active,
+#fc_window__controls > .el-dropdown > button:active {
color: #56585a;
}
@@ -188,4 +196,9 @@ export default {
margin-right: 15px;
}
+sup {
+ transform: translate(-10px, 5px) !important;
+ border: none !important;
+}
+
</style>
diff --git a/src-vue/src/components/NotificationButton.vue b/src-vue/src/components/NotificationButton.vue
new file mode 100644
index 00000000..4945f8c7
--- /dev/null
+++ b/src-vue/src/components/NotificationButton.vue
@@ -0,0 +1,77 @@
+<template>
+ <el-dropdown trigger="click" placement="bottom-end" max-height="280" popper-class="fc_popper">
+ <el-badge v-if="notifications.length != 0" :value="notifications.length" :max="9" class="item" type="primary">
+ <el-button color="white" icon="BellFilled" circle />
+ </el-badge>
+ <el-button v-else color="white" icon="BellFilled" circle />
+ <template #dropdown>
+ <el-dropdown-menu :key="counter">
+ <el-alert
+ v-if="notifications.length != 0"
+ v-for="(notification, i) in notifications"
+ :key="i"
+ :title="notification.title"
+ :description="notification.text"
+ :type="notification.type"
+ show-icon
+ style="width: 300px"
+ @close.stop="removeNotification(i)"
+ />
+ <el-result
+ v-else
+ icon="success"
+ :title="i18n.global.tc('notification.no_new.title')"
+ :sub-title="i18n.global.tc('notification.no_new.text')"
+ >
+ <template #icon>
+ </template>
+ </el-result>
+ </el-dropdown-menu>
+ </template>
+ </el-dropdown>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import {Notification} from '../plugins/modules/notifications';
+import {i18n} from "../main";
+
+export default defineComponent({
+ name: 'NotificationButton',
+ data: () => ({
+ // This variable is used as a key for the dropdown menu, so we can force it to refresh when a item is deleted.
+ counter: 0
+ }),
+ computed: {
+ i18n() {
+ return i18n
+ },
+ notifications(): Notification[] {
+ return this.$store.state.notifications.notifications;
+ }
+ },
+ methods: {
+ removeNotification(index: number) {
+ this.$store.commit('removeNotification', index);
+ // By refreshing the notifications list, we ensure the first notification get the index 0, ensuring this list
+ // is synchronized with the store list.
+ this.counter += 1;
+ }
+ }
+})
+</script>
+
+<style scoped>
+.el-dropdown {
+ height: 100%;
+ max-height: 300px;
+}
+
+.el-button {
+ margin: auto 25px auto 0 !important;
+}
+
+.el-alert {
+ margin: 5px 10px 5px 5px;
+}
+</style>
diff --git a/src-vue/src/i18n/lang/en.json b/src-vue/src/i18n/lang/en.json
index 2bf18c2e..6fa6a965 100644
--- a/src-vue/src/i18n/lang/en.json
+++ b/src-vue/src/i18n/lang/en.json
@@ -146,6 +146,11 @@
},
"notification": {
+ "no_new": {
+ "title": "Up-to-date",
+ "text": "Nothing to see here!"
+ },
+
"game_folder": {
"new": {
"title": "New game folder",
diff --git a/src-vue/src/i18n/lang/fr.json b/src-vue/src/i18n/lang/fr.json
index dbb34e80..c1ad181f 100644
--- a/src-vue/src/i18n/lang/fr.json
+++ b/src-vue/src/i18n/lang/fr.json
@@ -127,6 +127,11 @@
}
},
"notification": {
+ "no_new": {
+ "title": "Vous êtes à jour",
+ "text": "Rien à voir par ici !"
+ },
+
"game_folder": {
"new": {
"title": "Nouveau dossier",
diff --git a/src-vue/src/plugins/modules/notifications.ts b/src-vue/src/plugins/modules/notifications.ts
new file mode 100644
index 00000000..ed57f8af
--- /dev/null
+++ b/src-vue/src/plugins/modules/notifications.ts
@@ -0,0 +1,31 @@
+type NotificationType = 'success' | 'warning' | 'info' | 'error';
+
+export interface Notification {
+ title: string;
+ text: string;
+ type: NotificationType;
+}
+
+interface NotificationsStoreState {
+ notifications: Notification[];
+}
+
+
+/**
+ * This notification module is meant to host the list of notifications that have been fired while the application was
+ * not focused.
+ * This list is then used by the [NotificationButton] component to display notifications to user.
+ **/
+export const notificationsModule = {
+ state: () => ({
+ notifications: []
+ }) as NotificationsStoreState,
+ mutations: {
+ addNotification(state: NotificationsStoreState, payload: Notification) {
+ state.notifications.push(payload);
+ },
+ removeNotification(state: NotificationsStoreState, index: number): void {
+ state.notifications.splice(index, 1);
+ }
+ }
+ }
diff --git a/src-vue/src/plugins/store.ts b/src-vue/src/plugins/store.ts
index 854cd19e..56bf37e9 100644
--- a/src-vue/src/plugins/store.ts
+++ b/src-vue/src/plugins/store.ts
@@ -19,6 +19,7 @@ import { searchModule } from './modules/search';
import { i18n } from '../main';
import { pullRequestModule } from './modules/pull_requests';
import { showErrorNotification, showNotification } from '../utils/ui';
+import { notificationsModule } from './modules/notifications';
const persistentStore = new Store('flight-core-settings.json');
@@ -57,6 +58,7 @@ export const store = createStore<FlightCoreStore>({
modules: {
search: searchModule,
pullrequests: pullRequestModule,
+ notifications: notificationsModule
},
state(): FlightCoreStore {
return {
diff --git a/src-vue/src/style.css b/src-vue/src/style.css
index dfe682bf..e5c66328 100644
--- a/src-vue/src/style.css
+++ b/src-vue/src/style.css
@@ -50,6 +50,9 @@ body {
.el-popper {
width: 200px !important;
}
+.fc_popper {
+ width: auto !important;
+}
.el-popconfirm {
word-break: break-word;
diff --git a/src-vue/src/utils/ui.ts b/src-vue/src/utils/ui.ts
index b84d7666..4c735a9c 100644
--- a/src-vue/src/utils/ui.ts
+++ b/src-vue/src/utils/ui.ts
@@ -1,8 +1,10 @@
import { ElNotification, NotificationHandle } from "element-plus";
import { i18n } from "../main";
+import { store } from "../plugins/store";
/**
* Displays content to the user in the form of a notification appearing on screen bottom right.
+ * If the app is not focused when this is invoked, a notification is added to the notifications menu.
**/
function showNotification(
title: string,
@@ -10,6 +12,10 @@ function showNotification(
type: 'success' | 'warning' | 'error' | 'info' = 'success',
duration: number = 4500
): NotificationHandle {
+ if (!document.hasFocus()) {
+ store.commit('addNotification', {title, text: message, type});
+ }
+
return ElNotification({
title, message, type, duration,
position: 'bottom-right',