summaryrefslogtreecommitdiff
path: root/packages/frontend/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/frontend/src')
-rw-r--r--packages/frontend/src/pages/settings/general.vue22
-rw-r--r--packages/frontend/src/store.ts8
-rw-r--r--packages/frontend/src/ui/_common_/common.vue108
3 files changed, 114 insertions, 24 deletions
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index a487a4622a..0a53ed2c8d 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -93,6 +93,26 @@
</FormSection>
<FormSection>
+ <template #label>{{ i18n.ts.notificationDisplay }}</template>
+
+ <div class="_gaps_m">
+ <MkRadios v-model="notificationPosition">
+ <template #label>{{ i18n.ts.position }}</template>
+ <option value="leftTop"><i class="ti ti-align-box-left-top"></i> {{ i18n.ts.leftTop }}</option>
+ <option value="rightTop"><i class="ti ti-align-box-right-top"></i> {{ i18n.ts.rightTop }}</option>
+ <option value="leftBottom"><i class="ti ti-align-box-left-bottom"></i> {{ i18n.ts.leftBottom }}</option>
+ <option value="rightBottom"><i class="ti ti-align-box-right-bottom"></i> {{ i18n.ts.rightBottom }}</option>
+ </MkRadios>
+
+ <MkRadios v-model="notificationStackAxis">
+ <template #label>{{ i18n.ts.stackAxis }}</template>
+ <option value="vertical"><i class="ti ti-carousel-vertical"></i> {{ i18n.ts.vertical }}</option>
+ <option value="horizontal"><i class="ti ti-carousel-horizontal"></i> {{ i18n.ts.horizontal }}</option>
+ </MkRadios>
+ </div>
+ </FormSection>
+
+ <FormSection>
<MkSwitch v-model="aiChanMode">{{ i18n.ts.aiChanMode }}</MkSwitch>
</FormSection>
@@ -181,6 +201,8 @@ const useReactionPickerForContextMenu = computed(defaultStore.makeGetterSetter('
const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars'));
const aiChanMode = computed(defaultStore.makeGetterSetter('aiChanMode'));
const mediaListWithOneImageAppearance = computed(defaultStore.makeGetterSetter('mediaListWithOneImageAppearance'));
+const notificationPosition = computed(defaultStore.makeGetterSetter('notificationPosition'));
+const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis'));
watch(lang, () => {
miLocalStorage.setItem('lang', lang.value as string);
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index a4f0f65e14..710b08d9e0 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -314,6 +314,14 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device',
default: 'expand' as 'expand' | '16_9' | '1_1' | '2_3',
},
+ notificationPosition: {
+ where: 'device',
+ default: 'rightBottom' as 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom',
+ },
+ notificationStackAxis: {
+ where: 'device',
+ default: 'horizontal' as 'vertical' | 'horizontal',
+ },
}));
// TODO: 他のタブと永続化されたstateを同期
diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue
index 5a32c076a4..71a4285e9d 100644
--- a/packages/frontend/src/ui/_common_/common.vue
+++ b/packages/frontend/src/ui/_common_/common.vue
@@ -10,14 +10,16 @@
<XUpload v-if="uploads.length > 0"/>
<TransitionGroup
- tag="div" :class="$style.notifications"
+ tag="div" :class="[$style.notifications, $style[`notificationsPosition-${defaultStore.state.notificationPosition}`], $style[`notificationsStackAxis-${defaultStore.state.notificationStackAxis}`]]"
:move-class="defaultStore.state.animation ? $style.transition_notification_move : ''"
:enter-active-class="defaultStore.state.animation ? $style.transition_notification_enterActive : ''"
:leave-active-class="defaultStore.state.animation ? $style.transition_notification_leaveActive : ''"
:enter-from-class="defaultStore.state.animation ? $style.transition_notification_enterFrom : ''"
:leave-to-class="defaultStore.state.animation ? $style.transition_notification_leaveTo : ''"
>
- <XNotification v-for="notification in notifications" :key="notification.id" :notification="notification" :class="$style.notification"/>
+ <div v-for="notification in notifications" :key="notification.id" :class="$style.notification">
+ <XNotification :notification="notification"/>
+ </div>
</TransitionGroup>
<XStreamIndicator/>
@@ -30,7 +32,7 @@
</template>
<script lang="ts" setup>
-import { defineAsyncComponent } from 'vue';
+import { defineAsyncComponent, ref } from 'vue';
import * as misskey from 'misskey-js';
import { swInject } from './sw-inject';
import XNotification from './notification.vue';
@@ -85,7 +87,10 @@ if ($i) {
.transition_notification_leaveActive {
transition: opacity 0.3s, transform 0.3s !important;
}
-.transition_notification_enterFrom,
+.transition_notification_enterFrom {
+ opacity: 0;
+ transform: translateX(250px);
+}
.transition_notification_leaveTo {
opacity: 0;
transform: translateX(-250px);
@@ -94,35 +99,90 @@ if ($i) {
.notifications {
position: fixed;
z-index: 3900000;
- left: 0;
- width: 250px;
- top: 32px;
- padding: 0 32px;
+ padding: 0 var(--margin);
pointer-events: none;
- container-type: inline-size;
-}
+ display: flex;
-.notification {
- & + .notification {
- margin-top: 8px;
+ &.notificationsPosition-leftTop {
+ top: var(--margin);
+ left: 0;
+ }
+
+ &.notificationsPosition-rightTop {
+ top: var(--margin);
+ right: 0;
+ }
+
+ &.notificationsPosition-leftBottom {
+ bottom: calc(var(--minBottomSpacing) + var(--margin));
+ left: 0;
}
-}
-@media (max-width: 500px) {
- .notifications {
- top: initial;
+ &.notificationsPosition-rightBottom {
bottom: calc(var(--minBottomSpacing) + var(--margin));
- padding: 0 var(--margin);
- display: flex;
- flex-direction: column-reverse;
+ right: 0;
}
- .notification {
- & + .notification {
- margin-top: 0;
- margin-bottom: 8px;
+ &.notificationsStackAxis-vertical {
+ width: 250px;
+
+ &.notificationsPosition-leftTop,
+ &.notificationsPosition-rightTop {
+ flex-direction: column;
+
+ .notification {
+ & + .notification {
+ margin-top: 8px;
+ }
+ }
+ }
+
+ &.notificationsPosition-leftBottom,
+ &.notificationsPosition-rightBottom {
+ flex-direction: column-reverse;
+
+ .notification {
+ & + .notification {
+ margin-bottom: 8px;
+ }
+ }
}
}
+
+ &.notificationsStackAxis-horizontal {
+ width: 100%;
+
+ &.notificationsPosition-leftTop,
+ &.notificationsPosition-leftBottom {
+ flex-direction: row;
+
+ .notification {
+ & + .notification {
+ margin-left: 8px;
+ }
+ }
+ }
+
+ &.notificationsPosition-rightTop,
+ &.notificationsPosition-rightBottom {
+ flex-direction: row-reverse;
+
+ .notification {
+ & + .notification {
+ margin-right: 8px;
+ }
+ }
+ }
+
+ .notification {
+ width: 250px;
+ flex-shrink: 0;
+ }
+ }
+}
+
+.notification {
+ container-type: inline-size;
}
</style>