summaryrefslogtreecommitdiff
path: root/packages/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'packages/frontend')
-rw-r--r--packages/frontend/src/components/MkNotificationToast.vue68
-rw-r--r--packages/frontend/src/ui/_common_/common.vue72
-rw-r--r--packages/frontend/src/ui/_common_/notification.vue24
3 files changed, 90 insertions, 74 deletions
diff --git a/packages/frontend/src/components/MkNotificationToast.vue b/packages/frontend/src/components/MkNotificationToast.vue
deleted file mode 100644
index 39e8373e37..0000000000
--- a/packages/frontend/src/components/MkNotificationToast.vue
+++ /dev/null
@@ -1,68 +0,0 @@
-<template>
-<div class="mk-notification-toast" :style="{ zIndex }">
- <Transition :name="$store.state.animation ? 'notification-toast' : ''" appear @after-leave="$emit('closed')">
- <XNotification v-if="showing" :notification="notification" class="notification _acrylic"/>
- </Transition>
-</div>
-</template>
-
-<script lang="ts" setup>
-import { onMounted } from 'vue';
-import XNotification from '@/components/MkNotification.vue';
-import * as os from '@/os';
-
-defineProps<{
- notification: any; // TODO
-}>();
-
-const emit = defineEmits<{
- (ev: 'closed'): void;
-}>();
-
-const zIndex = os.claimZIndex('high');
-let showing = $ref(true);
-
-onMounted(() => {
- window.setTimeout(() => {
- showing = false;
- }, 6000);
-});
-</script>
-
-<style lang="scss" scoped>
-.notification-toast-enter-active, .notification-toast-leave-active {
- transition: opacity 0.3s, transform 0.3s !important;
-}
-.notification-toast-enter-from, .notification-toast-leave-to {
- opacity: 0;
- transform: translateX(-250px);
-}
-
-.mk-notification-toast {
- position: fixed;
- left: 0;
- width: 250px;
- top: 32px;
- padding: 0 32px;
- pointer-events: none;
- container-type: inline-size;
-
- @media (max-width: 700px) {
- top: initial;
- bottom: 112px;
- padding: 0 16px;
- }
-
- @media (max-width: 500px) {
- bottom: calc(env(safe-area-inset-bottom, 0px) + 92px);
- padding: 0 8px;
- }
-
- > .notification {
- height: 100%;
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
- border-radius: 8px;
- overflow: hidden;
- }
-}
-</style>
diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue
index 7f3fc0e4af..0333e20d0a 100644
--- a/packages/frontend/src/ui/_common_/common.vue
+++ b/packages/frontend/src/ui/_common_/common.vue
@@ -9,6 +9,10 @@
<XUpload v-if="uploads.length > 0"/>
+<TransitionGroup :name="$store.state.animation ? 'notification' : ''" tag="div" class="notifications">
+ <XNotification v-for="notification in notifications" :key="notification.id" :notification="notification" class="notification"/>
+</TransitionGroup>
+
<XStreamIndicator/>
<div v-if="pendingApiRequestsCount > 0" id="wait"></div>
@@ -19,8 +23,10 @@
</template>
<script lang="ts" setup>
-import { defineAsyncComponent } from 'vue';
+import { defineAsyncComponent, nextTick } from 'vue';
+import * as misskey from 'misskey-js';
import { swInject } from './sw-inject';
+import XNotification from './notification.vue';
import { popup, popups, pendingApiRequestsCount } from '@/os';
import { uploads } from '@/scripts/upload';
import * as sound from '@/scripts/sound';
@@ -33,7 +39,9 @@ const XUpload = defineAsyncComponent(() => import('./upload.vue'));
const dev = _DEV_;
-const onNotification = notification => {
+let notifications = $ref<misskey.entities.Notification[]>([]);
+
+function onNotification(notification) {
if ($i.mutingNotificationTypes.includes(notification.type)) return;
if (document.visibilityState === 'visible') {
@@ -41,13 +49,18 @@ const onNotification = notification => {
id: notification.id,
});
- popup(defineAsyncComponent(() => import('@/components/MkNotificationToast.vue')), {
- notification,
- }, {}, 'closed');
+ notifications.unshift(notification);
+ window.setTimeout(() => {
+ if (notifications.length > 3) notifications.pop();
+ }, 500);
+
+ window.setTimeout(() => {
+ notifications = notifications.filter(x => x.id !== notification.id);
+ }, 6000);
}
sound.play('notification');
-};
+}
if ($i) {
const connection = stream.useChannel('main', null, 'UI');
@@ -60,6 +73,53 @@ if ($i) {
}
</script>
+<style lang="scss" scoped>
+.notification-move, .notification-enter-active, .notification-leave-active {
+ transition: opacity 0.3s, transform 0.3s !important;
+}
+.notification-enter-from, .notification-leave-to {
+ opacity: 0;
+ transform: translateX(-250px);
+}
+
+.notifications {
+ position: fixed;
+ z-index: 3900000;
+ left: 0;
+ width: 250px;
+ top: 32px;
+ padding: 0 32px;
+ pointer-events: none;
+ container-type: inline-size;
+
+ > .notification {
+ & + .notification {
+ margin-top: 8px;
+ }
+ }
+
+ @media (max-width: 700px) {
+ top: initial;
+ bottom: 112px;
+ padding: 0 16px;
+ display: flex;
+ flex-direction: column-reverse;
+
+ > .notification {
+ & + .notification {
+ margin-top: 0;
+ margin-bottom: 8px;
+ }
+ }
+ }
+
+ @media (max-width: 500px) {
+ bottom: calc(env(safe-area-inset-bottom, 0px) + 92px);
+ padding: 0 8px;
+ }
+}
+</style>
+
<style lang="scss">
@keyframes dev-ticker-blink {
0% { opacity: 1; }
diff --git a/packages/frontend/src/ui/_common_/notification.vue b/packages/frontend/src/ui/_common_/notification.vue
new file mode 100644
index 0000000000..1f9c675a15
--- /dev/null
+++ b/packages/frontend/src/ui/_common_/notification.vue
@@ -0,0 +1,24 @@
+<template>
+<div :class="$style.root">
+ <XNotification :notification="notification" class="notification _acrylic"/>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { } from 'vue';
+import * as misskey from 'misskey-js';
+import XNotification from '@/components/MkNotification.vue';
+
+defineProps<{
+ notification: misskey.entities.Notification;
+}>();
+</script>
+
+<style lang="scss" module>
+.root {
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
+ border-radius: 8px;
+ overflow: clip;
+ contain: content;
+}
+</style>