いいかんじ

This commit is contained in:
mattyatea 2023-09-29 23:18:18 +09:00
parent 7dbbfb657c
commit 582a6948e5
88 changed files with 1215 additions and 552 deletions

View file

@ -93,6 +93,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<footer>
<div :class="$style.noteFooterInfo">
<div v-if="appearNote.updatedAt">
{{ i18n.ts.edited }}: <MkTime :time="appearNote.updatedAt" mode="detail"/>
</div>
<MkA :to="notePage(appearNote)">
<MkTime :time="appearNote.createdAt" mode="detail"/>
</MkA>

View file

@ -14,6 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/>
</div>
<div :class="$style.info">
<span v-if="note.updatedAt" style="margin-right: 0.5em;" :title="i18n.ts.edited"><i class="ti ti-pencil"></i></span>
<MkA :to="notePage(note)">
<MkTime :time="note.createdAt"/>
</MkA>

View file

@ -0,0 +1,78 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkModalWindow
ref="dialog"
:width="400"
:height="450"
:withOkButton="true"
:okButtonDisabled="false"
@ok="ok()"
@close="dialog?.close()"
@closed="emit('closed')"
>
<template #header>{{ i18n.ts.notificationSetting }}</template>
<MkSpacer :marginMin="20" :marginMax="28">
<div class="_gaps_m">
<MkInfo>{{ i18n.ts.notificationSettingDesc }}</MkInfo>
<div class="_buttons">
<MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton>
<MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton>
</div>
<MkSwitch v-for="ntype in notificationTypes" :key="ntype" v-model="typesMap[ntype].value">{{ i18n.t(`_notification._types.${ntype}`) }}</MkSwitch>
</div>
</MkSpacer>
</MkModalWindow>
</template>
<script lang="ts" setup>
import { ref, Ref } from 'vue';
import MkSwitch from './MkSwitch.vue';
import MkInfo from './MkInfo.vue';
import MkButton from './MkButton.vue';
import MkModalWindow from '@/components/MkModalWindow.vue';
import { notificationTypes } from '@/const.js';
import { i18n } from '@/i18n.js';
type TypesMap = Record<typeof notificationTypes[number], Ref<boolean>>
const emit = defineEmits<{
(ev: 'done', v: { excludeTypes: string[] }): void,
(ev: 'closed'): void,
}>();
const props = withDefaults(defineProps<{
excludeTypes?: typeof notificationTypes[number][];
}>(), {
excludeTypes: () => [],
});
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
const typesMap: TypesMap = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(!props.excludeTypes.includes(t)) }), {} as any);
function ok() {
emit('done', {
excludeTypes: (Object.keys(typesMap) as typeof notificationTypes[number][])
.filter(type => !typesMap[type].value),
});
if (dialog) dialog.close();
}
function disableAll() {
for (const type of notificationTypes) {
typesMap[type].value = false;
}
}
function enableAll() {
for (const type of notificationTypes) {
typesMap[type].value = true;
}
}
</script>

View file

@ -1,95 +0,0 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkModalWindow
ref="dialog"
:width="400"
:height="450"
:withOkButton="true"
:okButtonDisabled="false"
@ok="ok()"
@close="dialog?.close()"
@closed="emit('closed')"
>
<template #header>{{ i18n.ts.notificationSetting }}</template>
<MkSpacer :marginMin="20" :marginMax="28">
<div class="_gaps_m">
<template v-if="showGlobalToggle">
<MkSwitch v-model="useGlobalSetting">
{{ i18n.ts.useGlobalSetting }}
<template #caption>{{ i18n.ts.useGlobalSettingDesc }}</template>
</MkSwitch>
</template>
<template v-if="!useGlobalSetting">
<MkInfo>{{ i18n.ts.notificationSettingDesc }}</MkInfo>
<div class="_buttons">
<MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton>
<MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton>
</div>
<MkSwitch v-for="ntype in notificationTypes" :key="ntype" v-model="typesMap[ntype].value">{{ i18n.t(`_notification._types.${ntype}`) }}</MkSwitch>
</template>
</div>
</MkSpacer>
</MkModalWindow>
</template>
<script lang="ts" setup>
import { ref, Ref } from 'vue';
import MkSwitch from './MkSwitch.vue';
import MkInfo from './MkInfo.vue';
import MkButton from './MkButton.vue';
import MkModalWindow from '@/components/MkModalWindow.vue';
import { notificationTypes } from '@/const';
import { i18n } from '@/i18n.js';
type TypesMap = Record<typeof notificationTypes[number], Ref<boolean>>
const emit = defineEmits<{
(ev: 'done', v: { includingTypes: string[] | null }): void,
(ev: 'closed'): void,
}>();
const props = withDefaults(defineProps<{
includingTypes?: typeof notificationTypes[number][] | null;
showGlobalToggle?: boolean;
}>(), {
includingTypes: () => [],
showGlobalToggle: true,
});
let includingTypes = $computed(() => props.includingTypes?.filter(x => notificationTypes.includes(x)) ?? []);
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
const typesMap: TypesMap = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(includingTypes.includes(t)) }), {} as any);
let useGlobalSetting = $ref((includingTypes === null || includingTypes.length === 0) && props.showGlobalToggle);
function ok() {
if (useGlobalSetting) {
emit('done', { includingTypes: null });
} else {
emit('done', {
includingTypes: (Object.keys(typesMap) as typeof notificationTypes[number][])
.filter(type => typesMap[type].value),
});
}
if (dialog) dialog.close();
}
function disableAll() {
for (const type of notificationTypes) {
typesMap[type].value = false;
}
}
function enableAll() {
for (const type of notificationTypes) {
typesMap[type].value = true;
}
}
</script>

View file

@ -30,11 +30,11 @@ import MkNote from '@/components/MkNote.vue';
import { useStream } from '@/stream.js';
import { $i } from '@/account.js';
import { i18n } from '@/i18n.js';
import { notificationTypes } from '@/const';
import { notificationTypes } from '@/const.js';
import { infoImageUrl } from '@/instance.js';
const props = defineProps<{
includeTypes?: typeof notificationTypes[number][];
excludeTypes?: typeof notificationTypes[number][];
}>();
const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
@ -43,13 +43,12 @@ const pagination: Paging = {
endpoint: 'i/notifications' as const,
limit: 10,
params: computed(() => ({
includeTypes: props.includeTypes ?? undefined,
excludeTypes: props.includeTypes ? undefined : $i.mutingNotificationTypes,
excludeTypes: props.excludeTypes ?? undefined,
})),
};
const onNotification = (notification) => {
const isMuted = props.includeTypes ? !props.includeTypes.includes(notification.type) : $i.mutingNotificationTypes.includes(notification.type);
const isMuted = props.excludeTypes ? props.excludeTypes.includes(notification.type) : false;
if (isMuted || document.visibilityState === 'visible') {
useStream().send('readNotification');
}

View file

@ -0,0 +1,367 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<button
class="_button"
:class="[$style.root,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'
,}]" v-if="isFollowing"
@click="onClick"
>
<span v-if="props.user.notify === 'none'" :class="[{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }] "><i class="ti ti-bell"></i></span>
<span v-else-if="props.user.notify === 'normal'" :class="[{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]"><i class="ti ti-bell-off"></i></span>
</button>
</template>
<script lang="ts" setup>
import {computed, onBeforeUnmount, onMounted, ref, watch} from 'vue';
import * as Misskey from 'misskey-js';
import * as os from '@/os.js';
import {useStream} from '@/stream.js';
import {defaultStore} from "@/store.js";
let gaming = ref('');
const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode'));
const darkMode = computed(defaultStore.makeGetterSetter('darkMode'));
if (darkMode.value && gamingMode.value == true) {
gaming.value = 'dark';
} else if (!darkMode.value && gamingMode.value == true) {
gaming.value = 'light';
} else {
gaming.value = '';
}
watch(darkMode, () => {
if (darkMode.value && gamingMode.value == true) {
gaming.value = 'dark';
} else if (!darkMode.value && gamingMode.value == true) {
gaming.value = 'light';
} else {
gaming.value = '';
}
})
watch(gamingMode, () => {
if (darkMode.value && gamingMode.value == true) {
gaming.value = 'dark';
} else if (!darkMode.value && gamingMode.value == true) {
gaming.value = 'light';
} else {
gaming.value = '';
}
})
const props = withDefaults(defineProps<{
user: Misskey.entities.UserDetailed,
full?: boolean,
large?: boolean,
}>(), {
full: false,
large: false,
});
let isFollowing = $ref(props.user.isFollowing);
let notify = $ref(props.user.notify);
const connection = useStream().useChannel('main');
if (props.user.isFollowing == null) {
os.api('users/show', {
userId: props.user.id,
}).then(onFollowChange);
}
if (props.user.notify == null) {
os.api('users/show', {
userId: props.user.id,
}).then(onNotifyChange);
}
function onFollowChange(user: Misskey.entities.UserDetailed) {
if (user.id === props.user.id) {
isFollowing = user.isFollowing;
}
}
function onNotifyChange(user: Misskey.entities.UserDetailed) {
if (user.id === props.user.id) {
notify = user.notify;
console.log(props.user.notify)
}
}
async function onClick() {
try {
await os.apiWithDialog('following/update', {
userId: props.user.id,
notify: props.user.notify === 'normal' ? 'none' : 'normal',
}).then(() => {
props.user.notify = props.user.notify === 'normal' ? 'none' : 'normal';
});
}finally {
}
}
onMounted(() => {
connection.on('follow', onFollowChange);
connection.on('unfollow', onFollowChange);
});
onBeforeUnmount(() => {
connection.dispose();
});
</script>
<style lang="scss" module>
.root {
position: relative;
display: inline-block;
font-weight: bold;
color: var(--fgOnWhite);
border: solid 1px var(--accent);
padding: 0;
height: 31px;
font-size: 16px;
border-radius: 32px;
background: #fff;
vertical-align: bottom;
&.gamingDark {
color: black !important;
background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd);
background-size: 1800% 1800%;
-webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite;
-moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite;
animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite;
border: solid 1px black;
}
&.gamingLight {
color: white !important;
background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4);
background-size: 1800% 1800% !important;
-webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important;
-moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important;
animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important;
border: solid 1px white;
}
&.full {
padding: 0 8px 0 12px;
font-size: 14px;
&.gamingDark {
color: black;
background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd);
background-size: 1800% 1800%;
-webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite;
-moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite;
animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite;
}
&.gamingLight {
color: white;
background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4);
background-size: 1800% 1800% !important;
-webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important;
-moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important;
animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important;
}
}
&.large {
font-size: 16px;
height: 38px;
padding: 0 12px 0 16px;
}
&:not(.full) {
width: 31px;
}
&:focus-visible {
&:after {
content: "";
pointer-events: none;
position: absolute;
top: -5px;
right: -5px;
bottom: -5px;
left: -5px;
border: 2px solid var(--focus);
border-radius: 32px;
}
}
&:hover {
//background: mix($primary, #fff, 20);
}
&:active {
//background: mix($primary, #fff, 40);
}
&.active {
color: var(--fgOnAccent);
background: var(--accent);
&:hover {
background: var(--accentLighten);
border-color: var(--accentLighten);
}
&:active {
background: var(--accentDarken);
border-color: var(--accentDarken);
}
&.gamingDark:hover {
color: black;
background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd);
background-size: 1800% 1800%;
-webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite;
-moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite;
animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite;
border: solid 1px white;
}
&.gamingDark:active {
color: black;
background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd);
background-size: 1800% 1800%;
-webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite;
-moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite;
animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite;
border: solid 1px white;
}
&.gamingLight:hover {
background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4);
background-size: 1800% 1800% !important;
-webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important;
-moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important;
animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important;
border: solid 1px white;
}
&.gamingLight:active {
color: white;
background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4);
background-size: 1800% 1800% !important;
-webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important;
-moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important;
animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important;
border: solid 1px white;
}
&.gamingDark {
-webkit-text-fill-color: unset !important;
color: black;
border: solid 1px white;
background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd);
background-size: 1800% 1800%;
-webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite;
-moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite;
animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite;
}
&.gamingLight {
-webkit-text-fill-color: unset !important;
color: white;
border: solid 1px white;
background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4);
background-size: 1800% 1800% !important;
-webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important;
-moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important;
animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important;
}
}
}
.gamingDark {
color: black;
}
.gamingLight {
color: white;
}
@-webkit-keyframes AnimationLight {
0% {
background-position: 0% 50%
}
50% {
background-position: 100% 50%
}
100% {
background-position: 0% 50%
}
}
@-moz-keyframes AnimationLight {
0% {
background-position: 0% 50%
}
50% {
background-position: 100% 50%
}
100% {
background-position: 0% 50%
}
}
@keyframes AnimationLight {
0% {
background-position: 0% 50%
}
50% {
background-position: 100% 50%
}
100% {
background-position: 0% 50%
}
}
@-webkit-keyframes AnimationDark {
0% {
background-position: 0% 50%
}
50% {
background-position: 100% 50%
}
100% {
background-position: 0% 50%
}
}
@-moz-keyframes AnimationDark {
0% {
background-position: 0% 50%
}
50% {
background-position: 100% 50%
}
100% {
background-position: 0% 50%
}
}
@keyframes AnimationDark {
0% {
background-position: 0% 50%
}
50% {
background-position: 100% 50%
}
100% {
background-position: 0% 50%
}
}
</style>