wip
This commit is contained in:
@@ -7,6 +7,7 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
||||||
import type { JsonObject } from '@/misc/json-value.js';
|
import type { JsonObject } from '@/misc/json-value.js';
|
||||||
|
import { ChatService } from '@/core/ChatService.js';
|
||||||
import Channel, { type MiChannelService } from '../channel.js';
|
import Channel, { type MiChannelService } from '../channel.js';
|
||||||
|
|
||||||
class ChatChannel extends Channel {
|
class ChatChannel extends Channel {
|
||||||
@@ -17,6 +18,8 @@ class ChatChannel extends Channel {
|
|||||||
private otherId: string;
|
private otherId: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private chatService: ChatService,
|
||||||
|
|
||||||
id: string,
|
id: string,
|
||||||
connection: Channel['connection'],
|
connection: Channel['connection'],
|
||||||
) {
|
) {
|
||||||
@@ -36,6 +39,17 @@ class ChatChannel extends Channel {
|
|||||||
this.send(data.type, data.body);
|
this.send(data.type, data.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public onMessage(type: string, body: any) {
|
||||||
|
switch (type) {
|
||||||
|
case 'read':
|
||||||
|
if (this.otherId) {
|
||||||
|
this.chatService.readUserChatMessage(this.user!.id, this.otherId);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public dispose() {
|
public dispose() {
|
||||||
// Unsubscribe events
|
// Unsubscribe events
|
||||||
@@ -50,12 +64,14 @@ export class ChatChannelService implements MiChannelService<true> {
|
|||||||
public readonly kind = ChatChannel.kind;
|
public readonly kind = ChatChannel.kind;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private chatService: ChatService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public create(id: string, connection: Channel['connection']): ChatChannel {
|
public create(id: string, connection: Channel['connection']): ChatChannel {
|
||||||
return new ChatChannel(
|
return new ChatChannel(
|
||||||
|
this.chatService,
|
||||||
id,
|
id,
|
||||||
connection,
|
connection,
|
||||||
);
|
);
|
||||||
|
@@ -38,7 +38,7 @@ export function getScrollPosition(el: HTMLElement | null): number {
|
|||||||
|
|
||||||
export function onScrollTop(el: HTMLElement, cb: (topVisible: boolean) => unknown, tolerance = 1, once = false) {
|
export function onScrollTop(el: HTMLElement, cb: (topVisible: boolean) => unknown, tolerance = 1, once = false) {
|
||||||
// とりあえず評価してみる
|
// とりあえず評価してみる
|
||||||
const firstTopVisible = isTopVisible(el);
|
const firstTopVisible = isHeadVisible(el);
|
||||||
if (el.isConnected && firstTopVisible) {
|
if (el.isConnected && firstTopVisible) {
|
||||||
cb(firstTopVisible);
|
cb(firstTopVisible);
|
||||||
if (once) return null;
|
if (once) return null;
|
||||||
@@ -53,7 +53,7 @@ export function onScrollTop(el: HTMLElement, cb: (topVisible: boolean) => unknow
|
|||||||
const onScroll = () => {
|
const onScroll = () => {
|
||||||
if (!document.body.contains(el)) return;
|
if (!document.body.contains(el)) return;
|
||||||
|
|
||||||
const topVisible = isTopVisible(el, tolerance);
|
const topVisible = isHeadVisible(el, tolerance);
|
||||||
if (topVisible !== prevTopVisible) {
|
if (topVisible !== prevTopVisible) {
|
||||||
prevTopVisible = topVisible;
|
prevTopVisible = topVisible;
|
||||||
cb(topVisible);
|
cb(topVisible);
|
||||||
@@ -71,7 +71,7 @@ export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance = 1
|
|||||||
const container = getScrollContainer(el);
|
const container = getScrollContainer(el);
|
||||||
|
|
||||||
// とりあえず評価してみる
|
// とりあえず評価してみる
|
||||||
if (el.isConnected && isBottomVisible(el, tolerance, container)) {
|
if (el.isConnected && isTailVisible(el, tolerance, container)) {
|
||||||
cb();
|
cb();
|
||||||
if (once) return null;
|
if (once) return null;
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance = 1
|
|||||||
const containerOrWindow = container ?? window;
|
const containerOrWindow = container ?? window;
|
||||||
const onScroll = () => {
|
const onScroll = () => {
|
||||||
if (!document.body.contains(el)) return;
|
if (!document.body.contains(el)) return;
|
||||||
if (isBottomVisible(el, 1, container)) {
|
if (isTailVisible(el, 1, container)) {
|
||||||
cb();
|
cb();
|
||||||
if (once) removeListener();
|
if (once) removeListener();
|
||||||
}
|
}
|
||||||
@@ -132,12 +132,12 @@ export function scrollToBottom(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isTopVisible(el: HTMLElement, tolerance = 1): boolean {
|
export function isHeadVisible(el: HTMLElement, tolerance = 1): boolean {
|
||||||
const scrollTop = getScrollPosition(el);
|
const scrollTop = getScrollPosition(el);
|
||||||
return scrollTop <= tolerance;
|
return scrollTop <= tolerance;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isBottomVisible(el: HTMLElement, tolerance = 1, container = getScrollContainer(el)) {
|
export function isTailVisible(el: HTMLElement, tolerance = 1, container = getScrollContainer(el)) {
|
||||||
if (container) return el.scrollHeight <= container.clientHeight + Math.abs(container.scrollTop) + tolerance;
|
if (container) return el.scrollHeight <= container.clientHeight + Math.abs(container.scrollTop) + tolerance;
|
||||||
return el.scrollHeight <= window.innerHeight + window.scrollY + tolerance;
|
return el.scrollHeight <= window.innerHeight + window.scrollY + tolerance;
|
||||||
}
|
}
|
||||||
|
@@ -172,10 +172,6 @@ export default defineComponent({
|
|||||||
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1);
|
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.deny-move-transition > .list-move {
|
|
||||||
transition: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .list-enter-active {
|
> .list-enter-active {
|
||||||
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1), opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1);
|
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1), opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
}
|
}
|
||||||
|
@@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
import { computed, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, useTemplateRef, watch } from 'vue';
|
import { computed, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, useTemplateRef, watch } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { useDocumentVisibility } from '@@/js/use-document-visibility.js';
|
import { useDocumentVisibility } from '@@/js/use-document-visibility.js';
|
||||||
import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@@/js/scroll.js';
|
import { onScrollTop, isHeadVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isTailVisible } from '@@/js/scroll.js';
|
||||||
import type { ComputedRef } from 'vue';
|
import type { ComputedRef } from 'vue';
|
||||||
import type { MisskeyEntity } from '@/types/date-separated-list.js';
|
import type { MisskeyEntity } from '@/types/date-separated-list.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
@@ -97,6 +97,7 @@ const props = withDefaults(defineProps<{
|
|||||||
pagination: Paging;
|
pagination: Paging;
|
||||||
disableAutoLoad?: boolean;
|
disableAutoLoad?: boolean;
|
||||||
displayLimit?: number;
|
displayLimit?: number;
|
||||||
|
scrollReversed?: boolean;
|
||||||
}>(), {
|
}>(), {
|
||||||
displayLimit: 20,
|
displayLimit: 20,
|
||||||
});
|
});
|
||||||
@@ -349,7 +350,7 @@ const appearFetchMoreAhead = async (): Promise<void> => {
|
|||||||
fetchMoreAppearTimeout();
|
fetchMoreAppearTimeout();
|
||||||
};
|
};
|
||||||
|
|
||||||
const isTop = (): boolean => isBackTop.value || (props.pagination.reversed ? isBottomVisible : isTopVisible)(contentEl.value!, TOLERANCE);
|
const isHead = (): boolean => isBackTop.value || (props.pagination.reversed && !props.scrollReversed ? isTailVisible : isHeadVisible)(contentEl.value!, TOLERANCE);
|
||||||
|
|
||||||
watch(visibility, () => {
|
watch(visibility, () => {
|
||||||
if (visibility.value === 'hidden') {
|
if (visibility.value === 'hidden') {
|
||||||
@@ -364,7 +365,7 @@ watch(visibility, () => {
|
|||||||
timerForSetPause = null;
|
timerForSetPause = null;
|
||||||
} else {
|
} else {
|
||||||
isPausingUpdate = false;
|
isPausingUpdate = false;
|
||||||
if (isTop()) {
|
if (isHead()) {
|
||||||
executeQueue();
|
executeQueue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -376,16 +377,18 @@ watch(visibility, () => {
|
|||||||
* ストリーミングから降ってきたアイテムはこれで追加する
|
* ストリーミングから降ってきたアイテムはこれで追加する
|
||||||
* @param item アイテム
|
* @param item アイテム
|
||||||
*/
|
*/
|
||||||
const prepend = (item: MisskeyEntity): void => {
|
function prepend(item: MisskeyEntity): void {
|
||||||
if (items.value.size === 0) {
|
if (items.value.size === 0) {
|
||||||
items.value.set(item.id, item);
|
items.value.set(item.id, item);
|
||||||
fetching.value = false;
|
fetching.value = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTop() && !isPausingUpdate) unshiftItems([item]);
|
console.log(isHead(), isPausingUpdate);
|
||||||
|
|
||||||
|
if (isHead() && !isPausingUpdate) unshiftItems([item]);
|
||||||
else prependQueue(item);
|
else prependQueue(item);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新着アイテムをitemsの先頭に追加し、displayLimitを適用する
|
* 新着アイテムをitemsの先頭に追加し、displayLimitを適用する
|
||||||
|
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="800">
|
<MkSpacer :contentMax="800">
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<MkButton primary :class="$style.start" @click="start"><i class="ti ti-plus"></i> {{ i18n.ts.startChat }}</MkButton>
|
<MkButton primary :class="$style.start" @click="start"><i class="ti ti-plus"></i> {{ i18n.ts.startChat }}</MkButton>
|
||||||
@@ -39,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<MkLoading v-if="fetching"/>
|
<MkLoading v-if="fetching"/>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@@ -233,6 +233,10 @@ onMounted(() => {
|
|||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.root {
|
.root {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
border: solid 1px var(--MI_THEME-divider);
|
||||||
|
border-bottom: none;
|
||||||
|
border-radius: 14px 14px 0 0;
|
||||||
|
overflow: clip;
|
||||||
}
|
}
|
||||||
|
|
||||||
.textarea {
|
.textarea {
|
||||||
@@ -252,13 +256,13 @@ onMounted(() => {
|
|||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: var(--fg);
|
color: var(--MI_THEME-fg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: var(--panel);
|
background: var(--MI_THEME-panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
.file {
|
.file {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="[$style.root, { [$style.isMe]: isMe }]">
|
<div :class="[$style.root, { [$style.isMe]: isMe }]">
|
||||||
<MkAvatar :class="$style.avatar" :user="user" indicator link preview/>
|
<MkAvatar :class="$style.avatar" :user="user" link/>
|
||||||
<div :class="$style.body">
|
<div :class="$style.body">
|
||||||
<MkFukidashi :class="$style.fukidashi" :tail="isMe ? 'right' : 'left'" :accented="isMe">
|
<MkFukidashi :class="$style.fukidashi" :tail="isMe ? 'right' : 'left'" :accented="isMe">
|
||||||
<div v-if="!message.isDeleted" :class="$style.content">
|
<div v-if="!message.isDeleted" :class="$style.content">
|
||||||
@@ -113,6 +113,6 @@ function del(): void {
|
|||||||
|
|
||||||
.time {
|
.time {
|
||||||
font-size: 75%;
|
font-size: 75%;
|
||||||
opacity: 0.7;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -12,30 +12,30 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
|
|
||||||
<div ref="rootEl" :class="$style.root">
|
<div ref="rootEl" :class="$style.root">
|
||||||
<MkSpacer :contentMax="700">
|
<MkSpacer :contentMax="700">
|
||||||
<MkPagination v-if="pagination" ref="pagingComponent" :key="userId || roomId" :pagination="pagination" :disableAutoLoad="true">
|
<MkPagination v-if="pagination" ref="pagingComponent" :key="userId || roomId" :pagination="pagination" :disableAutoLoad="true" :scrollReversed="true">
|
||||||
<template #empty>
|
<template #empty>
|
||||||
<div class="_fullinfo">
|
<div class="_fullinfo">
|
||||||
<div>{{ i18n.ts.noMessagesYet }}</div>
|
<div>{{ i18n.ts.noMessagesYet }}</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #default="{ items: messages, fetching: pFetching }">
|
<template #default="{ items: messages }">
|
||||||
<MkDateSeparatedList
|
<TransitionGroup
|
||||||
v-if="messages.length > 0"
|
:enterActiveClass="prefer.s.animation ? $style.transition_x_enterActive : ''"
|
||||||
v-slot="{ item: message }"
|
:leaveActiveClass="prefer.s.animation ? $style.transition_x_leaveActive : ''"
|
||||||
:class="{ [$style['messages']]: true, 'deny-move-transition': pFetching }"
|
:enterFromClass="prefer.s.animation ? $style.transition_x_enterFrom : ''"
|
||||||
:items="messages"
|
:leaveToClass="prefer.s.animation ? $style.transition_x_leaveTo : ''"
|
||||||
direction="up"
|
:moveClass="prefer.s.animation ? $style.transition_x_move : ''"
|
||||||
reversed
|
tag="div" class="_gaps"
|
||||||
>
|
>
|
||||||
<XMessage :key="message.id" :message="message" :user="message.fromUserId === $i.id ? $i : user" :isRoom="room != null"/>
|
<XMessage v-for="message in messages.toReversed()" :key="message.id" :message="message" :user="message.fromUserId === $i.id ? $i : user" :isRoom="room != null"/>
|
||||||
</MkDateSeparatedList>
|
</TransitionGroup>
|
||||||
</template>
|
</template>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<MkSpacer :contentMax="700">
|
<div :class="$style.footer">
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<Transition name="fade">
|
<Transition name="fade">
|
||||||
<div v-show="showIndicator" :class="$style.new">
|
<div v-show="showIndicator" :class="$style.new">
|
||||||
@@ -46,16 +46,16 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</Transition>
|
</Transition>
|
||||||
<XForm v-if="!fetching" :user="user" :room="room" :class="$style.form"/>
|
<XForm v-if="!fetching" :user="user" :room="room" :class="$style.form"/>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</MkStickyContainer>
|
</MkStickyContainer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, shallowRef, computed, watch, onMounted, nextTick, onBeforeUnmount } from 'vue';
|
import { ref, useTemplateRef, computed, watch, onMounted, nextTick, onBeforeUnmount } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { isBottomVisible } from '@@/js/scroll.js';
|
import { isTailVisible } from '@@/js/scroll.js';
|
||||||
import XMessage from './room.message.vue';
|
import XMessage from './room.message.vue';
|
||||||
import XForm from './room.form.vue';
|
import XForm from './room.form.vue';
|
||||||
import type { Paging } from '@/components/MkPagination.vue';
|
import type { Paging } from '@/components/MkPagination.vue';
|
||||||
@@ -68,6 +68,7 @@ import { i18n } from '@/i18n.js';
|
|||||||
import { ensureSignin } from '@/i.js';
|
import { ensureSignin } from '@/i.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
import { definePage } from '@/page.js';
|
import { definePage } from '@/page.js';
|
||||||
|
import { prefer } from '@/preferences.js';
|
||||||
|
|
||||||
const $i = ensureSignin();
|
const $i = ensureSignin();
|
||||||
|
|
||||||
@@ -76,8 +77,8 @@ const props = defineProps<{
|
|||||||
roomId?: string;
|
roomId?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const rootEl = shallowRef<HTMLDivElement>();
|
const rootEl = useTemplateRef('rootEl');
|
||||||
const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
|
const pagingComponent = useTemplateRef('pagingComponent');
|
||||||
|
|
||||||
const fetching = ref(true);
|
const fetching = ref(true);
|
||||||
const user = ref<Misskey.entities.UserDetailed | null>(null);
|
const user = ref<Misskey.entities.UserDetailed | null>(null);
|
||||||
@@ -109,7 +110,7 @@ async function fetch() {
|
|||||||
pageEl: rootEl.value,
|
pageEl: rootEl.value,
|
||||||
};
|
};
|
||||||
connection.value = useStream().useChannel('chat', {
|
connection.value = useStream().useChannel('chat', {
|
||||||
other: user.value.id,
|
otherId: user.value.id,
|
||||||
});
|
});
|
||||||
}/* else {
|
}/* else {
|
||||||
user = null;
|
user = null;
|
||||||
@@ -140,15 +141,16 @@ async function fetch() {
|
|||||||
function onMessage(message) {
|
function onMessage(message) {
|
||||||
//sound.play('chat');
|
//sound.play('chat');
|
||||||
|
|
||||||
const _isBottom = isBottomVisible(rootEl, 64);
|
//const _isBottom = isBottomVisible(rootEl, 64);
|
||||||
|
|
||||||
pagingComponent.value.prepend(message);
|
pagingComponent.value.prepend(message);
|
||||||
if (message.userId !== $i.id && !document.hidden) {
|
if (message.userId !== $i.id && !window.document.hidden) {
|
||||||
connection.value?.send('read', {
|
connection.value?.send('read', {
|
||||||
id: message.id,
|
id: message.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
if (_isBottom) {
|
if (_isBottom) {
|
||||||
// Scroll to bottom
|
// Scroll to bottom
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
@@ -157,7 +159,7 @@ function onMessage(message) {
|
|||||||
} else if (message.userId !== $i.id) {
|
} else if (message.userId !== $i.id) {
|
||||||
// Notify
|
// Notify
|
||||||
notifyNewMessage();
|
notifyNewMessage();
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDeleted(id) {
|
function onDeleted(id) {
|
||||||
@@ -215,8 +217,22 @@ definePage(computed(() => !fetching.value ? user.value ? {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
.transition_x_move,
|
||||||
|
.transition_x_enterActive,
|
||||||
|
.transition_x_leaveActive {
|
||||||
|
transition: opacity 0.2s cubic-bezier(0,.5,.5,1), transform 0.2s cubic-bezier(0,.5,.5,1) !important;
|
||||||
|
}
|
||||||
|
.transition_x_enterFrom,
|
||||||
|
.transition_x_leaveTo {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(80px);
|
||||||
|
}
|
||||||
|
.transition_x_leaveActive {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
.root {
|
.root {
|
||||||
display: content;
|
min-height: 100cqh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.more {
|
.more {
|
||||||
@@ -241,10 +257,6 @@ definePage(computed(() => !fetching.value ? user.value ? {
|
|||||||
cursor: wait;
|
cursor: wait;
|
||||||
}
|
}
|
||||||
|
|
||||||
.messages {
|
|
||||||
padding: 16px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
@@ -273,12 +285,14 @@ definePage(computed(() => !fetching.value ? user.value ? {
|
|||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
.form {
|
.form {
|
||||||
max-height: 12em;
|
margin: 0 auto;
|
||||||
overflow-y: scroll;
|
width: 100%;
|
||||||
border-top: solid 0.5px var(--divider);
|
max-width: 700px;
|
||||||
border-bottom-left-radius: 0;
|
|
||||||
border-bottom-right-radius: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-enter-active, .fade-leave-active {
|
.fade-enter-active, .fade-leave-active {
|
||||||
|
@@ -136,6 +136,7 @@ export const PREF_DEF = {
|
|||||||
'clips',
|
'clips',
|
||||||
'drive',
|
'drive',
|
||||||
'followRequests',
|
'followRequests',
|
||||||
|
'chat',
|
||||||
'-',
|
'-',
|
||||||
'explore',
|
'explore',
|
||||||
'announcements',
|
'announcements',
|
||||||
|
Reference in New Issue
Block a user