forked from FoundKeyGang/FoundKey
wip
This commit is contained in:
parent
80eefa92ce
commit
0336d640ec
7 changed files with 155 additions and 56 deletions
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="note _panel"
|
class="tkcbzcuz _panel"
|
||||||
v-if="!muted"
|
v-if="!muted"
|
||||||
v-show="!isDeleted"
|
v-show="!isDeleted"
|
||||||
:tabindex="!isDeleted ? '-1' : null"
|
:tabindex="!isDeleted ? '-1' : null"
|
||||||
|
@ -858,7 +858,7 @@ export default defineComponent({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.note {
|
.tkcbzcuz {
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: box-shadow 0.1s ease;
|
transition: box-shadow 0.1s ease;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
|
@ -13,12 +13,10 @@ export default {
|
||||||
const viewHeight = container.clientHeight;
|
const viewHeight = container.clientHeight;
|
||||||
const height = container.scrollHeight;
|
const height = container.scrollHeight;
|
||||||
isBottom = (pos + viewHeight > height - 32);
|
isBottom = (pos + viewHeight > height - 32);
|
||||||
console.log(isBottom);
|
|
||||||
}, { passive: true });
|
}, { passive: true });
|
||||||
container.scrollTop = container.scrollHeight;
|
container.scrollTop = container.scrollHeight;
|
||||||
|
|
||||||
const ro = new ResizeObserver((entries, observer) => {
|
const ro = new ResizeObserver((entries, observer) => {
|
||||||
console.log(isBottom);
|
|
||||||
if (isBottom) {
|
if (isBottom) {
|
||||||
const height = container.scrollHeight;
|
const height = container.scrollHeight;
|
||||||
container.scrollTop = height;
|
container.scrollTop = height;
|
||||||
|
|
|
@ -120,6 +120,20 @@ export default defineComponent({
|
||||||
.hmjzthxl {
|
.hmjzthxl {
|
||||||
> .separator {
|
> .separator {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: auto;
|
||||||
|
width: calc(100% - 32px);
|
||||||
|
height: 1px;
|
||||||
|
background: var(--divider);
|
||||||
|
}
|
||||||
|
|
||||||
> .date {
|
> .date {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -130,6 +144,7 @@ export default defineComponent({
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: var(--dateLabelFg);
|
color: var(--dateLabelFg);
|
||||||
|
background: var(--panel);
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
&:first-child {
|
&:first-child {
|
||||||
|
|
44
src/client/ui/chat/header-clock.vue
Normal file
44
src/client/ui/chat/header-clock.vue
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<template>
|
||||||
|
<div class="_monospace">
|
||||||
|
<span>
|
||||||
|
<span v-text="hh"></span>
|
||||||
|
<span :style="{ visibility: showColon ? 'visible' : 'hidden' }">:</span>
|
||||||
|
<span v-text="mm"></span>
|
||||||
|
<span :style="{ visibility: showColon ? 'visible' : 'hidden' }">:</span>
|
||||||
|
<span v-text="ss"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import * as os from '@/os';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
clock: null,
|
||||||
|
hh: null,
|
||||||
|
mm: null,
|
||||||
|
ss: null,
|
||||||
|
showColon: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.tick();
|
||||||
|
this.clock = setInterval(this.tick, 1000);
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
clearInterval(this.clock);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
tick() {
|
||||||
|
const now = new Date();
|
||||||
|
this.hh = now.getHours().toString().padStart(2, '0');
|
||||||
|
this.mm = now.getMinutes().toString().padStart(2, '0');
|
||||||
|
this.ss = now.getSeconds().toString().padStart(2, '0');
|
||||||
|
this.showColon = now.getSeconds() % 2 === 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -62,21 +62,27 @@
|
||||||
|
|
||||||
<main class="main" @contextmenu.stop="onContextmenu">
|
<main class="main" @contextmenu.stop="onContextmenu">
|
||||||
<header class="header" ref="header" @click="onHeaderClick">
|
<header class="header" ref="header" @click="onHeaderClick">
|
||||||
<div v-if="tl === 'home'">
|
<div class="left">
|
||||||
|
<template v-if="tl === 'home'">
|
||||||
<Fa :icon="faHome" class="icon"/>
|
<Fa :icon="faHome" class="icon"/>
|
||||||
<div class="title">{{ $ts._timelines.home }}</div>
|
<div class="title">{{ $ts._timelines.home }}</div>
|
||||||
</div>
|
</template>
|
||||||
<div v-else-if="tl === 'local'">
|
<template v-else-if="tl === 'local'">
|
||||||
<Fa :icon="faShareAlt" class="icon"/>
|
<Fa :icon="faShareAlt" class="icon"/>
|
||||||
<div class="title">{{ $ts._timelines.local }}</div>
|
<div class="title">{{ $ts._timelines.local }}</div>
|
||||||
</div>
|
</template>
|
||||||
<div v-else-if="tl === 'social'">
|
<template v-else-if="tl === 'social'">
|
||||||
<Fa :icon="faShareAlt" class="icon"/>
|
<Fa :icon="faShareAlt" class="icon"/>
|
||||||
<div class="title">{{ $ts._timelines.social }}</div>
|
<div class="title">{{ $ts._timelines.social }}</div>
|
||||||
</div>
|
</template>
|
||||||
<div v-else-if="tl === 'global'">
|
<template v-else-if="tl === 'global'">
|
||||||
<Fa :icon="faShareAlt" class="icon"/>
|
<Fa :icon="faShareAlt" class="icon"/>
|
||||||
<div class="title">{{ $ts._timelines.global }}</div>
|
<div class="title">{{ $ts._timelines.global }}</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="right">
|
||||||
|
<XHeaderClock/>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
|
@ -105,6 +111,7 @@ import XCommon from '../_common_/common.vue';
|
||||||
import XSide from './side.vue';
|
import XSide from './side.vue';
|
||||||
import XTimeline from './timeline.vue';
|
import XTimeline from './timeline.vue';
|
||||||
import XPostForm from './post-form.vue';
|
import XPostForm from './post-form.vue';
|
||||||
|
import XHeaderClock from './header-clock.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { sidebarDef } from '@/sidebar';
|
import { sidebarDef } from '@/sidebar';
|
||||||
|
|
||||||
|
@ -115,6 +122,7 @@ export default defineComponent({
|
||||||
XSide, // NOTE: dynamic importするとAsyncComponentWrapperが間に入るせいでref取得できなくて面倒になる
|
XSide, // NOTE: dynamic importするとAsyncComponentWrapperが間に入るせいでref取得できなくて面倒になる
|
||||||
XTimeline,
|
XTimeline,
|
||||||
XPostForm,
|
XPostForm,
|
||||||
|
XHeaderClock,
|
||||||
},
|
},
|
||||||
|
|
||||||
provide() {
|
provide() {
|
||||||
|
@ -260,6 +268,29 @@ export default defineComponent({
|
||||||
border-top: solid 1px var(--divider);
|
border-top: solid 1px var(--divider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> .left, > .right {
|
||||||
|
> .item, > .menu {
|
||||||
|
height: ($header-height - ($padding * 2));
|
||||||
|
width: ($header-height - ($padding * 2));
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-right: 4px;
|
||||||
|
//opacity: 0.6;
|
||||||
|
position: relative;
|
||||||
|
line-height: initial;
|
||||||
|
|
||||||
|
> i {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
color: var(--indicator);
|
||||||
|
font-size: 8px;
|
||||||
|
line-height: 8px;
|
||||||
|
animation: blink 1s infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
> .left {
|
> .left {
|
||||||
> .account {
|
> .account {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -276,26 +307,6 @@ export default defineComponent({
|
||||||
|
|
||||||
> .right {
|
> .right {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
|
||||||
> .item {
|
|
||||||
height: ($header-height - ($padding * 2));
|
|
||||||
width: ($header-height - ($padding * 2));
|
|
||||||
padding: 10px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin-right: 4px;
|
|
||||||
//opacity: 0.6;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
> i {
|
|
||||||
position: absolute;
|
|
||||||
top: 8px;
|
|
||||||
right: 8px;
|
|
||||||
color: var(--indicator);
|
|
||||||
font-size: 8px;
|
|
||||||
line-height: 8px;
|
|
||||||
animation: blink 1s infinite;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,18 +369,19 @@ export default defineComponent({
|
||||||
|
|
||||||
> .header {
|
> .header {
|
||||||
$padding: 8px;
|
$padding: 8px;
|
||||||
|
display: flex;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
height: $header-height;
|
height: $header-height;
|
||||||
padding: $padding;
|
padding: $padding;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
line-height: ($header-height - ($padding * 2));
|
line-height: ($header-height - ($padding * 2));
|
||||||
font-weight: bold;
|
|
||||||
background-color: var(--panel);
|
background-color: var(--panel);
|
||||||
border-bottom: solid 1px var(--divider);
|
border-bottom: solid 1px var(--divider);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
> div {
|
> .left {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
> .icon {
|
> .icon {
|
||||||
height: ($header-height - ($padding * 2));
|
height: ($header-height - ($padding * 2));
|
||||||
|
@ -380,10 +392,15 @@ export default defineComponent({
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> .right {
|
||||||
|
margin-left: auto;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .footer {
|
> .footer {
|
||||||
padding: 16px;
|
padding: 0 16px 16px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .body {
|
> .body {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="note"
|
class="vfzoeqcg"
|
||||||
v-if="!muted"
|
v-if="!muted"
|
||||||
v-show="!isDeleted"
|
v-show="!isDeleted"
|
||||||
:tabindex="!isDeleted ? '-1' : null"
|
:tabindex="!isDeleted ? '-1' : null"
|
||||||
:class="{ renote: isRenote }"
|
:class="{ renote: isRenote, operating }"
|
||||||
v-hotkey="keymap"
|
v-hotkey="keymap"
|
||||||
>
|
>
|
||||||
<XSub :note="appearNote.reply" class="reply-to" v-if="appearNote.reply"/>
|
<XSub :note="appearNote.reply" class="reply-to" v-if="appearNote.reply"/>
|
||||||
|
@ -171,6 +171,7 @@ export default defineComponent({
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
isDeleted: false,
|
isDeleted: false,
|
||||||
muted: false,
|
muted: false,
|
||||||
|
operating: false,
|
||||||
faEdit, faBolt, faTimes, faBullhorn, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faBiohazard, faPlug, faSatelliteDish
|
faEdit, faBolt, faTimes, faBullhorn, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faBiohazard, faPlug, faSatelliteDish
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -439,16 +440,19 @@ export default defineComponent({
|
||||||
|
|
||||||
reply(viaKeyboard = false) {
|
reply(viaKeyboard = false) {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
|
this.operating = true;
|
||||||
os.post({
|
os.post({
|
||||||
reply: this.appearNote,
|
reply: this.appearNote,
|
||||||
animation: !viaKeyboard,
|
animation: !viaKeyboard,
|
||||||
}, () => {
|
}, () => {
|
||||||
|
this.operating = false;
|
||||||
this.focus();
|
this.focus();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
renote(viaKeyboard = false) {
|
renote(viaKeyboard = false) {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
|
this.operating = true;
|
||||||
this.blur();
|
this.blur();
|
||||||
os.modalMenu([{
|
os.modalMenu([{
|
||||||
text: this.$ts.renote,
|
text: this.$ts.renote,
|
||||||
|
@ -468,6 +472,8 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}], this.$refs.renoteButton, {
|
}], this.$refs.renoteButton, {
|
||||||
viaKeyboard
|
viaKeyboard
|
||||||
|
}).then(() => {
|
||||||
|
this.operating = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -494,10 +500,11 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
react(viaKeyboard = false) {
|
async react(viaKeyboard = false) {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
|
this.operating = true;
|
||||||
this.blur();
|
this.blur();
|
||||||
os.popup(import('@/components/emoji-picker.vue'), {
|
const { dispose } = await os.popup(import('@/components/emoji-picker.vue'), {
|
||||||
src: this.$refs.reactButton,
|
src: this.$refs.reactButton,
|
||||||
asReactionPicker: true
|
asReactionPicker: true
|
||||||
}, {
|
}, {
|
||||||
|
@ -508,9 +515,13 @@ export default defineComponent({
|
||||||
reaction: reaction
|
reaction: reaction
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.focus();
|
|
||||||
},
|
},
|
||||||
}, 'closed');
|
closed: () => {
|
||||||
|
this.operating = false;
|
||||||
|
this.focus();
|
||||||
|
dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
reactDirectly(reaction) {
|
reactDirectly(reaction) {
|
||||||
|
@ -734,9 +745,13 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
menu(viaKeyboard = false) {
|
menu(viaKeyboard = false) {
|
||||||
|
this.operating = true;
|
||||||
os.modalMenu(this.getMenu(), this.$refs.menuButton, {
|
os.modalMenu(this.getMenu(), this.$refs.menuButton, {
|
||||||
viaKeyboard
|
viaKeyboard
|
||||||
}).then(this.focus);
|
}).then(() => {
|
||||||
|
this.operating = false;
|
||||||
|
this.focus();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
showRenoteMenu(viaKeyboard = false) {
|
showRenoteMenu(viaKeyboard = false) {
|
||||||
|
@ -857,10 +872,8 @@ export default defineComponent({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.note {
|
.vfzoeqcg {
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: box-shadow 0.1s ease;
|
|
||||||
overflow: hidden;
|
|
||||||
contain: content;
|
contain: content;
|
||||||
|
|
||||||
// これらの指定はパフォーマンス向上には有効だが、ノートの高さは一定でないため、
|
// これらの指定はパフォーマンス向上には有効だが、ノートの高さは一定でないため、
|
||||||
|
@ -879,9 +892,11 @@ export default defineComponent({
|
||||||
background: rgba(0, 0, 0, 0.05);
|
background: rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover > .article > .main > .footer {
|
&:hover, &.operating {
|
||||||
|
> .article > .main > .footer {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.renote {
|
&.renote {
|
||||||
background: rgba(128, 255, 0, 0.05);
|
background: rgba(128, 255, 0, 0.05);
|
||||||
|
@ -983,8 +998,8 @@ export default defineComponent({
|
||||||
> .avatar {
|
> .avatar {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: block;
|
display: block;
|
||||||
//position: sticky;
|
position: sticky;
|
||||||
//top: 72px;
|
top: 12px;
|
||||||
margin: 0 14px 0 0;
|
margin: 0 14px 0 0;
|
||||||
width: 46px;
|
width: 46px;
|
||||||
height: 46px;
|
height: 46px;
|
||||||
|
@ -1122,5 +1137,9 @@ export default defineComponent({
|
||||||
.muted {
|
.muted {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<XNotes ref="tl" :pagination="pagination" @queue="$emit('queue', $event)" v-follow="pagination.reversed"/>
|
<XNotes class="dbiokgaf" ref="tl" :pagination="pagination" @queue="$emit('queue', $event)" v-follow="pagination.reversed"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -188,3 +188,9 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.dbiokgaf {
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
Loading…
Reference in a new issue