Compare commits

..

32 Commits

Author SHA1 Message Date
syuilo
c91eef0030 12.65.6 2020-12-31 12:17:12 +09:00
syuilo
7bf9d726d0 ピン留めされたノートを詳細表示しないように 2020-12-31 12:14:56 +09:00
syuilo
1946ff8ed4 Improve note auto collapse algorithm 2020-12-31 12:12:24 +09:00
syuilo
b4d85d4f69 🎨 2020-12-31 11:46:07 +09:00
syuilo
a03702d2bd Better note auto collapse algorithm 2020-12-31 11:45:58 +09:00
syuilo
71d7de4989 Update vue 🚀 2020-12-31 11:45:35 +09:00
syuilo
35d9e13dbb Improve emoji picker usability 2020-12-31 11:45:27 +09:00
syuilo
b427842679 12.65.5 2020-12-31 03:04:24 +09:00
syuilo
4ae172be57 Add blur MFM syntax 2020-12-31 03:02:09 +09:00
syuilo
e6705b1a65 Add MFM syntax 2020-12-31 02:51:51 +09:00
syuilo
bc22cabdb5 🎨 2020-12-31 02:43:24 +09:00
syuilo
7128b9f16a 🎨 2020-12-31 00:22:20 +09:00
syuilo
8c0490fef1 インスタンス情報に統計追加したり 2020-12-31 00:11:06 +09:00
syuilo
882a81636d 🎨 2020-12-30 19:26:47 +09:00
syuilo
24b9be76ba 🎨 2020-12-30 19:24:01 +09:00
syuilo
e763c6e661 プロモーション増殖バグ修正 2020-12-30 17:31:59 +09:00
syuilo
899e2c73d7 12.65.4 2020-12-30 13:14:02 +09:00
syuilo
10cb15b000 nanka iroiro 2020-12-30 13:07:16 +09:00
syuilo
873d4bd707 clean up 2020-12-30 11:08:41 +09:00
syuilo
97dea72c94 サイトのタイトルが変わらない問題を修正 2020-12-30 11:01:33 +09:00
syuilo
7d49f260b8 12.65.3 2020-12-30 10:17:51 +09:00
syuilo
3055e6d8c7 localeとthemeのキャッシュクリア実装 2020-12-30 10:15:16 +09:00
syuilo
2c93246860 Tweak note collapse threshold 2020-12-30 10:03:57 +09:00
syuilo
360c820b9d Resolve #3620 2020-12-30 09:58:57 +09:00
syuilo
87847c6ed5 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-12-30 09:27:23 +09:00
syuilo
4874f54d4d 🎨 2020-12-30 09:27:19 +09:00
dependabot-preview[bot]
ff73efcc08 Merge pull request #7040 from syuilo/dependabot/npm_and_yarn/vue-color-2.8.1 2020-12-29 20:30:32 +00:00
dependabot-preview[bot]
4ee64cbd9e Merge pull request #7039 from syuilo/dependabot/npm_and_yarn/typescript-4.1.3 2020-12-29 20:30:29 +00:00
dependabot-preview[bot]
0c40a86fca Bump vue-color from 2.7.1 to 2.8.1
Bumps [vue-color](https://github.com/xiaokaike/vue-color) from 2.7.1 to 2.8.1.
- [Release notes](https://github.com/xiaokaike/vue-color/releases)
- [Commits](https://github.com/xiaokaike/vue-color/compare/v2.7.1...v2.8.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-29 20:22:07 +00:00
dependabot-preview[bot]
f92eed0549 Bump typescript from 4.1.2 to 4.1.3
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.1.2 to 4.1.3.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.1.2...v4.1.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-29 20:22:00 +00:00
dependabot-preview[bot]
69ed8cc409 Merge pull request #7038 from syuilo/dependabot/npm_and_yarn/re2-1.15.9 2020-12-29 20:21:08 +00:00
dependabot-preview[bot]
4fff1279db Bump re2 from 1.15.8 to 1.15.9
Bumps [re2](https://github.com/uhop/node-re2) from 1.15.8 to 1.15.9.
- [Release notes](https://github.com/uhop/node-re2/releases)
- [Commits](https://github.com/uhop/node-re2/compare/1.15.8...1.15.9)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-29 20:13:45 +00:00
33 changed files with 1298 additions and 324 deletions

View File

@@ -1,5 +1,6 @@
_lang_: "日本語"
headlineMisskey: "ノートでつながるネットワーク"
introMisskey: "ようこそMisskeyは、オープンソースの分散型マイクロブログサービスです。\n「ート」を作成して、いま起こっていることを共有したり、あなたについて皆に発信しよう📡\n「リアクション」機能で、皆のートに素早く反応を追加することもできます👍\n新しい世界を探検しよう🚀"
monthAndDay: "{month}月 {day}日"
search: "検索"
@@ -49,6 +50,7 @@ copyUsername: "ユーザー名をコピー"
searchUser: "ユーザーを検索"
reply: "返信"
loadMore: "もっと見る"
showMore: "もっと見る"
youGotNewFollower: "フォローされました"
receiveFollowRequest: "フォローリクエストされました"
followRequestAccepted: "フォローが承認されました"
@@ -671,6 +673,10 @@ wide: "広い"
narrow: "狭い"
reloadToApplySetting: "設定はページリロード後に反映されます。今すぐリロードしますか?"
showTitlebar: "タイトルバーを表示する"
clearCache: "キャッシュをクリア"
onlineUsersCount: "{n}人がオンライン"
nUsers: "{n}ユーザー"
nNotes: "{n}ノート"
_aboutMisskey:
about: "Misskeyはsyuiloによって2014年から開発されている、オープンソースのソフトウェアです。"
@@ -730,11 +736,19 @@ _mfm:
bounce: "アニメーション(バウンド)"
bounceDescription: "ぽよんぽよん弾むようなアニメーションを与えます。"
shake: "アニメーション(ぶるぶる)"
shakeDescription: "ぶるぶるるアニメーションを与えます。"
shakeDescription: "ぶるぶる震えるアニメーションを与えます。"
twitch: "アニメーション(ブレ)"
twitchDescription: "激しくブレるアニメーションを与えます。"
spin: "アニメーション(回転)"
spinDescription: "回転するアニメーションを与えます。"
x2: "大きく"
x2Description: "内容を大きく表示します。"
x3: "とても大きく"
x3Description: "内容をとても大きく表示します。"
x4: "究極に大きく"
x4Description: "内容を究極に大きく表示します。"
blur: "ぼかし"
blurDescription: "内容をぼかすことができます。ポインターを上に乗せるとはっきり見えるようになります。"
_reversi:
reversi: "リバーシ"
@@ -1013,6 +1027,7 @@ _widgets:
postForm: "投稿フォーム"
slideshow: "スライドショー"
button: "ボタン"
onlineUsers: "オンラインユーザー"
_cw:
hide: "隠す"

View File

@@ -1,7 +1,7 @@
{
"name": "misskey",
"author": "syuilo <syuilotan@yahoo.co.jp>",
"version": "12.65.2",
"version": "12.65.6",
"codename": "indigo",
"repository": {
"type": "git",
@@ -105,7 +105,7 @@
"@types/websocket": "1.0.1",
"@types/ws": "7.4.0",
"@typescript-eslint/parser": "4.10.0",
"@vue/compiler-sfc": "3.0.3",
"@vue/compiler-sfc": "3.0.5",
"abort-controller": "3.0.0",
"apexcharts": "3.22.3",
"autobind-decorator": "2.4.0",
@@ -204,7 +204,7 @@
"qrcode": "1.4.4",
"random-seed": "0.3.0",
"ratelimiter": "3.4.1",
"re2": "1.15.8",
"re2": "1.15.9",
"recaptcha-promise": "1.0.0",
"reconnecting-websocket": "4.4.0",
"redis": "3.0.2",
@@ -237,14 +237,14 @@
"tslint": "6.1.3",
"tslint-sonarts": "1.9.0",
"typeorm": "0.2.29",
"typescript": "4.1.2",
"typescript": "4.1.3",
"ulid": "2.3.0",
"url-loader": "4.1.1",
"uuid": "8.3.2",
"v-debounce": "0.1.2",
"vanilla-tilt": "1.7.0",
"vue": "3.0.3",
"vue-color": "2.7.1",
"vue": "3.0.5",
"vue-color": "2.8.1",
"vue-json-pretty": "1.7.1",
"vue-loader": "16.0.0",
"vue-prism-editor": "2.0.0-alpha.2",

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -49,7 +49,7 @@ export default defineComponent({
const el = this.$slots.default({
item: item
})[0];
el.key = item.id;
if (el.key == null && item.id) el.key = item.id;
if (
i != this.items.length - 1 &&

View File

@@ -2,7 +2,7 @@
<MkModal ref="modal" :src="src" @click="$refs.modal.close()" @closed="$emit('closed')">
<div class="omfetrab _popup" :class="['w' + width, 'h' + height, { big }]">
<input ref="search" class="search" :class="{ filled: q != null && q != '' }" v-model.trim="q" :placeholder="$ts.search" @paste.stop="paste" @keyup.enter="done()">
<div class="emojis">
<div class="emojis" ref="emojis">
<section class="result">
<div v-if="searchResultCustom.length > 0">
<button v-for="emoji in searchResultCustom"
@@ -180,6 +180,8 @@ export default defineComponent({
watch: {
q() {
this.$refs.emojis.scrollTop = 0;
if (this.q == null || this.q === '') {
this.searchResultCustom = [];
this.searchResultUnicode = [];

View File

@@ -0,0 +1,34 @@
<template>
<div class="xfbouadm" v-if="meta" :style="{ backgroundImage: `url(${ meta.backgroundImageUrl })` }">
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import * as os from '@/os';
export default defineComponent({
components: {
},
data() {
return {
meta: null,
};
},
created() {
os.api('meta', { detail: true }).then(meta => {
this.meta = meta;
});
},
});
</script>
<style lang="scss" scoped>
.xfbouadm {
background-position: center;
background-size: cover;
}
</style>

View File

@@ -11,6 +11,11 @@ export default defineComponent({
required: false,
default: 'span',
},
textTag: {
type: String,
required: false,
default: null,
},
},
render() {
let str = this.src;
@@ -32,6 +37,6 @@ export default defineComponent({
str = str.substr(nextBracketClose + 1);
}
return h(this.tag, parsed.map(x => typeof x === 'string' ? x : this.$slots[x.arg]()));
return h(this.tag, parsed.map(x => typeof x === 'string' ? (this.textTag ? h(this.textTag, x) : x) : this.$slots[x.arg]()));
}
});

View File

@@ -14,6 +14,15 @@ export default defineComponent({
</script>
<style lang="scss">
._mfm_blur_ {
filter: blur(6px);
transition: filter 0.3s;
&:hover {
filter: blur(0px);
}
}
@keyframes mfm-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }

View File

@@ -129,6 +129,23 @@ export default defineComponent({
style = `transform: ${transform};`;
break;
}
case 'x2': {
style = `font-size: 200%;`;
break;
}
case 'x3': {
style = `font-size: 400%;`;
break;
}
case 'x4': {
style = `font-size: 600%;`;
break;
}
case 'blur': {
return h('span', {
class: '_mfm_blur_',
}, genEl(token.children));
}
}
if (style == null) {
return h('span', {}, ['[', token.node.props.name, ...genEl(token.children), ']']);

View File

@@ -46,7 +46,7 @@
<Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/>
<XCwButton v-model:value="showContent" :note="appearNote"/>
</p>
<div class="content" v-show="appearNote.cw == null || showContent">
<div class="content" :class="{ collapsed }" v-show="appearNote.cw == null || showContent">
<div class="text">
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ $ts.private }})</span>
<MkA class="reply" v-if="appearNote.replyId" :to="`/notes/${appearNote.replyId}`"><Fa :icon="faReply"/></MkA>
@@ -59,6 +59,9 @@
<XPoll v-if="appearNote.poll" :note="appearNote" ref="pollViewer" class="poll"/>
<MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="detail" class="url-preview"/>
<div class="renote" v-if="appearNote.renote"><XNotePreview :note="appearNote.renote"/></div>
<button v-if="collapsed" class="fade _button" @click="collapsed = false">
<span>{{ $ts.showMore }}</span>
</button>
</div>
<MkA v-if="appearNote.channel && !inChannel" class="channel" :to="`/channels/${appearNote.channel.id}`"><Fa :icon="faSatelliteDish"/> {{ appearNote.channel.name }}</MkA>
</div>
@@ -174,6 +177,7 @@ export default defineComponent({
conversation: [],
replies: [],
showContent: false,
collapsed: false,
isDeleted: false,
muted: false,
faEdit, faBolt, faTimes, faBullhorn, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faBiohazard, faPlug, faSatelliteDish
@@ -273,6 +277,12 @@ export default defineComponent({
this.connection = os.stream;
}
this.collapsed = !this.detail && this.appearNote.cw == null && this.appearNote.text && (
(this.appearNote.text.split('\n').length > 9) ||
(this.appearNote.text.length > 500)
);
this.muted = await checkWordMute(this.appearNote, this.$i, this.$store.state.mutedWords);
// plugin
if (noteViewInterruptors.length > 0) {
let result = this.note;
@@ -282,8 +292,6 @@ export default defineComponent({
this.$emit('update:note', Object.freeze(result));
}
this.muted = await checkWordMute(this.appearNote, this.$i, this.$store.state.mutedWords);
if (this.detail) {
os.api('notes/children', {
noteId: this.appearNote.id,
@@ -1038,6 +1046,37 @@ export default defineComponent({
}
> .content {
&.collapsed {
position: relative;
max-height: 9em;
overflow: hidden;
> .fade {
display: block;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 64px;
background: linear-gradient(0deg, var(--panel), var(--X15));
> span {
display: inline-block;
background: var(--panel);
padding: 6px 10px;
font-size: 0.8em;
border-radius: 999px;
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
}
&:hover {
> span {
background: var(--panelHighlight);
}
}
}
}
> .text {
overflow-wrap: break-word;

View File

@@ -1,6 +1,6 @@
<template>
<XWindow ref="window"
:initial-width="700"
:initial-width="500"
:initial-height="500"
:can-resize="true"
:close-right="true"

View File

@@ -91,10 +91,6 @@ if (_DEV_) {
// タッチデバイスでCSSの:hoverを機能させる
document.addEventListener('touchend', () => {}, { passive: true });
if (localStorage.theme == null) {
applyTheme(require('@/themes/l-light.json5'));
}
//#region SEE: https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
// TODO: いつの日にか消したい
const vh = window.innerHeight * 0.01;
@@ -204,7 +200,7 @@ watch(defaultStore.reactiveState.darkMode, (darkMode) => {
const themes = builtinThemes.concat(ColdDeviceStorage.get('themes'));
applyTheme(themes.find(x => x.id === (darkMode ? ColdDeviceStorage.get('darkTheme') : ColdDeviceStorage.get('lightTheme'))));
});
});
}, { immediate: localStorage.theme == null });
//#region Sync dark mode
if (ColdDeviceStorage.get('syncDeviceDarkMode')) {

View File

@@ -23,6 +23,18 @@
</FormGroup>
<FormLink v-if="meta.tosUrl" :to="meta.tosUrl" external>{{ $ts.tos }}</FormLink>
<FormGroup v-if="stats">
<template #label>{{ $ts.statistics }}</template>
<FormKeyValueView>
<template #key>{{ $ts.users }}</template>
<template #value>{{ number(stats.originalUsersCount) }}</template>
</FormKeyValueView>
<FormKeyValueView>
<template #key>{{ $ts.notes }}</template>
<template #value>{{ number(stats.originalNotesCount) }}</template>
</FormKeyValueView>
</FormGroup>
</FormBase>
</template>
@@ -35,6 +47,7 @@ import FormBase from '@/components/form/base.vue';
import FormGroup from '@/components/form/group.vue';
import FormKeyValueView from '@/components/form/key-value-view.vue';
import * as os from '@/os';
import number from '@/filters/number';
export default defineComponent({
components: {
@@ -52,7 +65,7 @@ export default defineComponent({
},
version,
instanceName,
serverInfo: null,
stats: null,
faInfoCircle
}
},
@@ -62,6 +75,16 @@ export default defineComponent({
return this.$instance;
},
},
created() {
os.api('stats').then(stats => {
this.stats = stats;
});
},
methods: {
number
}
});
</script>

View File

@@ -22,7 +22,7 @@
<XPostForm :channel="channel" class="post-form _content _panel _vMargin" fixed v-if="$i"/>
<XTimeline class="_content _vMargin" src="channel" :channel="channelId" @before="before" @after="after"/>
<XTimeline class="_content _vMargin _noGap_" src="channel" :channel="channelId" @before="before" @after="after"/>
</div>
</template>

View File

@@ -145,6 +145,46 @@
</div>
</div>
</div>
<div class="_section">
<div class="_title">{{ $ts._mfm.x2 }}</div>
<div class="_content">
<p>{{ $ts._mfm.x2Description }}</p>
<div class="preview _panel">
<Mfm :text="preview_x2"/>
<MkTextarea v-model:value="preview_x2"><span>MFM</span></MkTextarea>
</div>
</div>
</div>
<div class="_section">
<div class="_title">{{ $ts._mfm.x3 }}</div>
<div class="_content">
<p>{{ $ts._mfm.x3Description }}</p>
<div class="preview _panel">
<Mfm :text="preview_x3"/>
<MkTextarea v-model:value="preview_x3"><span>MFM</span></MkTextarea>
</div>
</div>
</div>
<div class="_section">
<div class="_title">{{ $ts._mfm.x4 }}</div>
<div class="_content">
<p>{{ $ts._mfm.x4Description }}</p>
<div class="preview _panel">
<Mfm :text="preview_x4"/>
<MkTextarea v-model:value="preview_x4"><span>MFM</span></MkTextarea>
</div>
</div>
</div>
<div class="_section">
<div class="_title">{{ $ts._mfm.blur }}</div>
<div class="_content">
<p>{{ $ts._mfm.blurDescription }}</p>
<div class="preview _panel">
<Mfm :text="preview_blur"/>
<MkTextarea v-model:value="preview_blur"><span>MFM</span></MkTextarea>
</div>
</div>
</div>
<div class="_section">
<div class="_title">{{ $ts._mfm.jelly }}</div>
<div class="_content">
@@ -255,6 +295,10 @@ export default defineComponent({
preview_twitch: `[twitch 🍮]`,
preview_spin: `[spin 🍮] [spin.left 🍮] [spin.alternate 🍮]\n[spin.x 🍮] [spin.x,left 🍮] [spin.x,alternate 🍮]\n[spin.y 🍮] [spin.y,left 🍮] [spin.y,alternate 🍮]`,
preview_flip: `[flip ${this.$ts._mfm.dummy}]\n[flip.v ${this.$ts._mfm.dummy}]\n[flip.h,v ${this.$ts._mfm.dummy}]`,
preview_x2: `[x2 🍮]`,
preview_x3: `[x3 🍮]`,
preview_x4: `[x4 🍮]`,
preview_blur: `[blur ${this.$ts._mfm.dummy}]`,
}
},
});

View File

@@ -27,6 +27,9 @@
<FormLink :active="page === 'api'" replace to="/settings/api"><template #icon><Fa :icon="faKey"/></template>API</FormLink>
<FormLink :active="page === 'other'" replace to="/settings/other"><template #icon><Fa :icon="faEllipsisH"/></template>{{ $ts.other }}</FormLink>
</FormGroup>
<FormGroup>
<FormButton @click="clear">{{ $ts.clearCache }}</FormButton>
</FormGroup>
<FormGroup>
<FormButton @click="logout" danger>{{ $ts.logout }}</FormButton>
</FormGroup>
@@ -126,6 +129,11 @@ export default defineComponent({
logout: () => {
signout();
},
clear: () => {
localStorage.removeItem('locale');
localStorage.removeItem('theme');
location.reload();
},
faPalette, faPlug, faUser, faListUl, faLock, faLaugh, faCommentSlash, faMusic, faBell, faCogs, faEllipsisH, faBan, faShareAlt, faLockOpen, faKey, faBoxes, faEnvelope,
};
},

View File

@@ -80,7 +80,7 @@
</div>
<template v-if="page === 'index'">
<div v-if="user.pinnedNotes.length > 0" class="_vMargin">
<XNote v-for="note in user.pinnedNotes" class="note _vMargin" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :detail="true" :pinned="true"/>
<XNote v-for="note in user.pinnedNotes" class="note _vMargin" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/>
</div>
<div class="_vMargin">
<XUserTimeline :user="user"/>
@@ -195,7 +195,7 @@
<template v-if="page === 'index'">
<div class="_content _vMargin">
<div v-if="user.pinnedNotes.length > 0" class="_vMargin">
<XNote v-for="note in user.pinnedNotes" class="note _vMargin" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :detail="true" :pinned="true"/>
<XNote v-for="note in user.pinnedNotes" class="note _vMargin" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/>
</div>
<XPhotos :user="user" :key="user.id" class="_vMargin"/>
<XActivity :user="user" :key="user.id" class="_vMargin"/>

View File

@@ -0,0 +1,307 @@
<template>
<div class="rsqzvsbo" v-if="meta">
<div class="top">
<MkFeaturedPhotos class="bg"/>
<XTimeline class="tl"/>
<div class="shape"></div>
<img src="/assets/misskey.svg" class="misskey"/>
<div class="emojis">
<MkEmoji :normal="true" :no-style="true" emoji="👍"/>
<MkEmoji :normal="true" :no-style="true" emoji="❤"/>
<MkEmoji :normal="true" :no-style="true" emoji="😆"/>
<MkEmoji :normal="true" :no-style="true" emoji="🎉"/>
<MkEmoji :normal="true" :no-style="true" emoji="🍮"/>
</div>
<div class="main _panel">
<div class="bg">
<div class="fade"></div>
</div>
<div class="fg">
<h1>
<img class="logo" v-if="meta.logoImageUrl" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span>
</h1>
<div class="about">
<div class="desc" v-html="meta.description || $ts.headlineMisskey"></div>
</div>
<div class="action">
<MkButton @click="signup()" inline primary>{{ $ts.signup }}</MkButton>
<MkButton @click="signin()" inline>{{ $ts.login }}</MkButton>
</div>
<div class="status" v-if="onlineUsersCount && stats">
<div>
<I18n :src="$ts.nUsers" text-tag="span" class="users">
<template #n><b>{{ number(stats.originalUsersCount) }}</b></template>
</I18n>
<I18n :src="$ts.nNotes" text-tag="span" class="notes">
<template #n><b>{{ number(stats.originalNotesCount) }}</b></template>
</I18n>
</div>
<I18n :src="$ts.onlineUsersCount" text-tag="span" class="online">
<template #n><b>{{ onlineUsersCount }}</b></template>
</I18n>
</div>
<button class="_button _acrylic menu" @click="showMenu"><Fa :icon="faEllipsisH"/></button>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { faEllipsisH, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
import { toUnicode } from 'punycode';
import XSigninDialog from '@/components/signin-dialog.vue';
import XSignupDialog from '@/components/signup-dialog.vue';
import MkButton from '@/components/ui/button.vue';
import XNote from '@/components/note.vue';
import MkFeaturedPhotos from '@/components/featured-photos.vue';
import XTimeline from './welcome.timeline.vue';
import { host, instanceName } from '@/config';
import * as os from '@/os';
import number from '@/filters/number';
export default defineComponent({
components: {
MkButton,
XNote,
MkFeaturedPhotos,
XTimeline,
},
data() {
return {
host: toUnicode(host),
instanceName,
meta: null,
stats: null,
tags: [],
onlineUsersCount: null,
faEllipsisH
};
},
created() {
os.api('meta', { detail: true }).then(meta => {
this.meta = meta;
});
os.api('stats').then(stats => {
this.stats = stats;
});
os.api('get-online-users-count').then(res => {
this.onlineUsersCount = res.count;
});
os.api('hashtags/list', {
sort: '+mentionedLocalUsers',
limit: 8
}).then(tags => {
this.tags = tags;
});
},
methods: {
signin() {
os.popup(XSigninDialog, {
autoSet: true
}, {}, 'closed');
},
signup() {
os.popup(XSignupDialog, {
autoSet: true
}, {}, 'closed');
},
showMenu(ev) {
os.modalMenu([{
text: this.$t('aboutX', { x: instanceName }),
icon: faInfoCircle,
action: () => {
os.pageWindow('/about');
}
}, {
text: this.$ts.aboutMisskey,
icon: faInfoCircle,
action: () => {
os.pageWindow('/about-misskey');
}
}, null, {
text: this.$ts.help,
icon: faQuestionCircle,
action: () => {
os.pageWindow('/docs');
}
}], ev.currentTarget || ev.target);
},
number
}
});
</script>
<style lang="scss" scoped>
.rsqzvsbo {
> .top {
display: flex;
text-align: center;
min-height: 100vh;
box-sizing: border-box;
padding: 16px;
> .bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
> .tl {
position: absolute;
top: 0;
bottom: 0;
right: 64px;
margin: auto;
width: 500px;
height: calc(100% - 128px);
overflow: hidden;
-webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
@media (max-width: 1200px) {
display: none;
}
}
> .shape {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--accent);
clip-path: polygon(0% 0%, 50% 0%, 15% 100%, 0% 100%);
}
> .misskey {
position: absolute;
top: 42px;
left: 42px;
width: 160px;
@media (max-width: 450px) {
width: 130px;
}
}
> .emojis {
position: absolute;
bottom: 32px;
left: 35px;
> * {
margin-right: 8px;
}
@media (max-width: 1200px) {
display: none;
}
}
> .main {
position: relative;
width: min(480px, 100%);
margin: auto auto auto 128px;
box-shadow: 0 12px 32px rgb(0 0 0 / 25%);
@media (max-width: 1200px) {
margin: auto;
}
> .bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 128px;
background-position: center;
background-size: cover;
opacity: 0.75;
> .fade {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 128px;
background: linear-gradient(0deg, var(--panel), var(--X15));
}
}
> .fg {
position: relative;
z-index: 1;
> h1 {
display: block;
margin: 0;
padding: 32px 32px 24px 32px;
> .logo {
vertical-align: bottom;
max-height: 120px;
}
}
> .about {
padding: 0 32px;
}
> .action {
padding: 32px;
> * {
line-height: 28px;
}
}
> .status {
border-top: solid 1px var(--divider);
padding: 32px;
font-size: 90%;
> div {
> span:not(:last-child) {
padding-right: 1em;
margin-right: 1em;
border-right: solid 1px var(--divider);
}
}
> .online {
::v-deep(b) {
color: #41b781;
}
::v-deep(span) {
opacity: 0.7;
}
}
}
> .menu {
position: absolute;
top: 16px;
right: 16px;
width: 32px;
height: 32px;
border-radius: 8px;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,238 @@
<template>
<div class="rsqzvsbo" v-if="meta">
<div class="top">
<MkFeaturedPhotos class="bg"/>
<XTimeline class="tl"/>
<div class="shape"></div>
<div class="main">
<h1>
<img class="logo" v-if="meta.logoImageUrl" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span>
</h1>
<div class="about">
<div class="desc" v-html="meta.description || $ts.headlineMisskey"></div>
</div>
<div class="action">
<MkButton class="signup" @click="signup()" inline primary>{{ $ts.signup }}</MkButton>
<MkButton class="signin" @click="signin()" inline>{{ $ts.login }}</MkButton>
</div>
<div class="status" v-if="onlineUsersCount && stats">
<div>
<I18n :src="$ts.nUsers" text-tag="span" class="users">
<template #n><b>{{ number(stats.originalUsersCount) }}</b></template>
</I18n>
<I18n :src="$ts.nNotes" text-tag="span" class="notes">
<template #n><b>{{ number(stats.originalNotesCount) }}</b></template>
</I18n>
</div>
<I18n :src="$ts.onlineUsersCount" text-tag="span" class="online">
<template #n><b>{{ onlineUsersCount }}</b></template>
</I18n>
</div>
</div>
<img src="/assets/misskey.svg" class="misskey"/>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { faEllipsisH, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
import { toUnicode } from 'punycode';
import XSigninDialog from '@/components/signin-dialog.vue';
import XSignupDialog from '@/components/signup-dialog.vue';
import MkButton from '@/components/ui/button.vue';
import XNote from '@/components/note.vue';
import MkFeaturedPhotos from '@/components/featured-photos.vue';
import XTimeline from './welcome.timeline.vue';
import { host, instanceName } from '@/config';
import * as os from '@/os';
import number from '@/filters/number';
export default defineComponent({
components: {
MkButton,
XNote,
XTimeline,
MkFeaturedPhotos,
},
data() {
return {
host: toUnicode(host),
instanceName,
meta: null,
stats: null,
tags: [],
onlineUsersCount: null,
faEllipsisH
};
},
created() {
os.api('meta', { detail: true }).then(meta => {
this.meta = meta;
});
os.api('stats').then(stats => {
this.stats = stats;
});
os.api('get-online-users-count').then(res => {
this.onlineUsersCount = res.count;
});
os.api('hashtags/list', {
sort: '+mentionedLocalUsers',
limit: 8
}).then(tags => {
this.tags = tags;
});
},
methods: {
signin() {
os.popup(XSigninDialog, {
autoSet: true
}, {}, 'closed');
},
signup() {
os.popup(XSignupDialog, {
autoSet: true
}, {}, 'closed');
},
showMenu(ev) {
os.modalMenu([{
text: this.$t('aboutX', { x: instanceName }),
icon: faInfoCircle,
action: () => {
os.pageWindow('/about');
}
}, {
text: this.$ts.aboutMisskey,
icon: faInfoCircle,
action: () => {
os.pageWindow('/about-misskey');
}
}, null, {
text: this.$ts.help,
icon: faQuestionCircle,
action: () => {
os.pageWindow('/docs');
}
}], ev.currentTarget || ev.target);
},
number
}
});
</script>
<style lang="scss" scoped>
.rsqzvsbo {
> .top {
min-height: 100vh;
box-sizing: border-box;
> .bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
> .tl {
position: absolute;
top: 0;
bottom: 0;
right: 64px;
margin: auto;
width: 500px;
height: calc(100% - 128px);
overflow: hidden;
-webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
}
> .shape {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--accent);
clip-path: polygon(0% 0%, 40% 0%, 22% 100%, 0% 100%);
}
> .misskey {
position: absolute;
bottom: 64px;
left: 64px;
width: 160px;
}
> .main {
position: relative;
width: min(450px, 100%);
padding: 64px;
color: #fff;
font-size: 1.1em;
@media (max-width: 1200px) {
margin: auto;
}
> h1 {
display: block;
margin: 0 0 32px 0;
padding: 0;
> .logo {
vertical-align: bottom;
max-height: 100px;
}
}
> .about {
padding: 0;
}
> .action {
margin: 32px 0;
> * {
line-height: 32px;
}
> .signup {
background: var(--panel);
color: var(--fg);
}
> .signin {
background: var(--accent);
color: inherit;
}
}
> .status {
margin: 32px 0;
border-top: solid 1px rgba(255, 255, 255, 0.5);
font-size: 90%;
> div {
padding: 16px 0;
> span:not(:last-child) {
padding-right: 1em;
margin-right: 1em;
border-right: solid 1px rgba(255, 255, 255, 0.5);
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,307 @@
<template>
<div class="rsqzvsbo" v-if="meta">
<div class="top">
<MkFeaturedPhotos class="bg"/>
<div class="fade"></div>
<div class="emojis">
<MkEmoji :normal="true" :no-style="true" emoji="👍"/>
<MkEmoji :normal="true" :no-style="true" emoji="❤"/>
<MkEmoji :normal="true" :no-style="true" emoji="😆"/>
<MkEmoji :normal="true" :no-style="true" emoji="🎉"/>
<MkEmoji :normal="true" :no-style="true" emoji="🍮"/>
</div>
<div class="main">
<img src="/assets/misskey.svg" class="misskey"/>
<div class="form _panel">
<div class="bg">
<div class="fade"></div>
</div>
<div class="fg">
<h1>
<img class="logo" v-if="meta.logoImageUrl" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span>
</h1>
<div class="about">
<div class="desc" v-html="meta.description || $ts.headlineMisskey"></div>
</div>
<div class="action">
<MkButton @click="signup()" inline primary>{{ $ts.signup }}</MkButton>
<MkButton @click="signin()" inline>{{ $ts.login }}</MkButton>
</div>
<div class="status" v-if="onlineUsersCount && stats">
<div>
<I18n :src="$ts.nUsers" text-tag="span" class="users">
<template #n><b>{{ number(stats.originalUsersCount) }}</b></template>
</I18n>
<I18n :src="$ts.nNotes" text-tag="span" class="notes">
<template #n><b>{{ number(stats.originalNotesCount) }}</b></template>
</I18n>
</div>
<I18n :src="$ts.onlineUsersCount" text-tag="span" class="online">
<template #n><b>{{ onlineUsersCount }}</b></template>
</I18n>
</div>
<button class="_button _acrylic menu" @click="showMenu"><Fa :icon="faEllipsisH"/></button>
</div>
</div>
<nav class="nav">
<MkA to="/announcements">{{ $ts.announcements }}</MkA>
<MkA to="/explore">{{ $ts.explore }}</MkA>
<MkA to="/channels">{{ $ts.channel }}</MkA>
<MkA to="/featured">{{ $ts.featured }}</MkA>
</nav>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { faEllipsisH, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
import { toUnicode } from 'punycode';
import XSigninDialog from '@/components/signin-dialog.vue';
import XSignupDialog from '@/components/signup-dialog.vue';
import MkButton from '@/components/ui/button.vue';
import XNote from '@/components/note.vue';
import MkFeaturedPhotos from '@/components/featured-photos.vue';
import XTimeline from './welcome.timeline.vue';
import { host, instanceName } from '@/config';
import * as os from '@/os';
import number from '@/filters/number';
export default defineComponent({
components: {
MkButton,
XNote,
MkFeaturedPhotos,
XTimeline,
},
data() {
return {
host: toUnicode(host),
instanceName,
meta: null,
stats: null,
tags: [],
onlineUsersCount: null,
faEllipsisH
};
},
created() {
os.api('meta', { detail: true }).then(meta => {
this.meta = meta;
});
os.api('stats').then(stats => {
this.stats = stats;
});
os.api('get-online-users-count').then(res => {
this.onlineUsersCount = res.count;
});
os.api('hashtags/list', {
sort: '+mentionedLocalUsers',
limit: 8
}).then(tags => {
this.tags = tags;
});
},
methods: {
signin() {
os.popup(XSigninDialog, {
autoSet: true
}, {}, 'closed');
},
signup() {
os.popup(XSignupDialog, {
autoSet: true
}, {}, 'closed');
},
showMenu(ev) {
os.modalMenu([{
text: this.$t('aboutX', { x: instanceName }),
icon: faInfoCircle,
action: () => {
os.pageWindow('/about');
}
}, {
text: this.$ts.aboutMisskey,
icon: faInfoCircle,
action: () => {
os.pageWindow('/about-misskey');
}
}, null, {
text: this.$ts.help,
icon: faQuestionCircle,
action: () => {
os.pageWindow('/docs');
}
}], ev.currentTarget || ev.target);
},
number
}
});
</script>
<style lang="scss" scoped>
.rsqzvsbo {
> .top {
display: flex;
text-align: center;
min-height: 100vh;
box-sizing: border-box;
padding: 16px;
> .bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
> .fade {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.25);
}
> .emojis {
position: absolute;
bottom: 32px;
left: 35px;
> * {
margin-right: 8px;
}
@media (max-width: 1200px) {
display: none;
}
}
> .main {
position: relative;
width: min(460px, 100%);
margin: auto;
> .misskey {
width: 150px;
margin-bottom: 16px;
@media (max-width: 450px) {
width: 130px;
}
}
> .form {
position: relative;
box-shadow: 0 12px 32px rgb(0 0 0 / 25%);
> .bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 128px;
background-position: center;
background-size: cover;
opacity: 0.75;
> .fade {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 128px;
background: linear-gradient(0deg, var(--panel), var(--X15));
}
}
> .fg {
position: relative;
z-index: 1;
> h1 {
display: block;
margin: 0;
padding: 32px 32px 24px 32px;
> .logo {
vertical-align: bottom;
max-height: 120px;
}
}
> .about {
padding: 0 32px;
}
> .action {
padding: 32px;
> * {
line-height: 28px;
}
}
> .status {
border-top: solid 1px var(--divider);
padding: 32px;
font-size: 90%;
> div {
> span:not(:last-child) {
padding-right: 1em;
margin-right: 1em;
border-right: solid 1px var(--divider);
}
}
> .online {
::v-deep(b) {
color: #41b781;
}
::v-deep(span) {
opacity: 0.7;
}
}
}
> .menu {
position: absolute;
top: 16px;
right: 16px;
width: 32px;
height: 32px;
border-radius: 8px;
}
}
}
> .nav {
position: relative;
z-index: 2;
margin-top: 20px;
color: #fff;
text-shadow: 0 0 8px black;
font-size: 0.9em;
> *:not(:last-child) {
margin-right: 1.5em;
}
}
}
}
}
</style>

View File

@@ -1,181 +0,0 @@
<template>
<div class="rsqzvsbo _section" v-if="meta">
<div class="overview _monospace" v-if="stats">
<div class="stats">
<div><span>Users</span><span>{{ number(stats.originalUsersCount) }}</span></div>
<div><span>Notes</span><span>{{ number(stats.originalNotesCount) }}</span></div>
<div><span>Reactions</span><span>{{ number(stats.reactionsCount) }}</span></div>
</div>
<div class="tags">
<MkA class="tag" v-for="tag in tags" :to="`/tags/${encodeURIComponent(tag.tag)}`">#{{ tag.tag }}</MkA>
</div>
</div>
<template v-if="meta.pinnedClipId">
<h2># {{ $ts.pinnedNotes }}</h2>
<MkPagination :pagination="clipPagination" #default="{items}">
<XNote class="kmkqjgkl" v-for="note in items" :note="note" :key="note.id"/>
</MkPagination>
</template>
<template v-else>
<h2># {{ $ts.featured }}</h2>
<MkPagination :pagination="featuredPagination" #default="{items}">
<XNote class="kmkqjgkl" v-for="note in items" :note="note" :key="note.id"/>
</MkPagination>
</template>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { toUnicode } from 'punycode';
import XSigninDialog from '@/components/signin-dialog.vue';
import XSignupDialog from '@/components/signup-dialog.vue';
import MkButton from '@/components/ui/button.vue';
import XNote from '@/components/note.vue';
import MkPagination from '@/components/ui/pagination.vue';
import { host, instanceName } from '@/config';
import * as os from '@/os';
import number from '@/filters/number';
export default defineComponent({
components: {
MkButton,
XNote,
MkPagination,
},
data() {
return {
host: toUnicode(host),
instanceName,
meta: null,
stats: null,
tags: [],
clipPagination: {
endpoint: 'clips/notes',
limit: 10,
params: () => ({
clipId: this.meta.pinnedClipId,
})
},
featuredPagination: {
endpoint: 'notes/featured',
limit: 10,
offsetMode: true
},
};
},
created() {
os.api('meta', { detail: true }).then(meta => {
this.meta = meta;
});
os.api('stats').then(stats => {
this.stats = stats;
});
os.api('hashtags/list', {
sort: '+mentionedLocalUsers',
limit: 8
}).then(tags => {
this.tags = tags;
});
},
methods: {
signin() {
os.popup(XSigninDialog, {
autoSet: true
}, {}, 'closed');
},
signup() {
os.popup(XSignupDialog, {
autoSet: true
}, {}, 'closed');
},
number
}
});
</script>
<style lang="scss" scoped>
.rsqzvsbo {
text-align: center;
> h2 {
display: inline-block;
color: #fff;
margin: 16px;
padding: 8px 12px;
background: rgba(0, 0, 0, 0.5);
}
> .overview {
> .stats, > .tags {
display: inline-block;
vertical-align: top;
width: 530px;
padding: 32px;
margin: 16px;
box-sizing: border-box;
@media (max-width: 800px) {
display: block;
width: 100%;
margin: 12px 0;
}
}
> .stats {
background: var(--accent);
border-radius: 12px;
color: #fff;
font-size: 1.5em;
> div {
display: flex;
> span:first-child {
opacity: 0.7;
font-weight: bold;
}
> span:last-child {
margin-left: auto;
}
}
}
> .tags {
background: var(--panel);
border-radius: 12px;
color: var(--fg);
font-size: 1.5em;
> .tag {
margin-right: 1em;
}
}
}
}
.kmkqjgkl {
display: inline-block;
vertical-align: middle;
width: 530px;
margin: 16px;
box-sizing: border-box;
text-align: left;
box-shadow: 0 6px 46px rgb(0 0 0 / 25%);
border-radius: 12px;
@media (max-width: 800px) {
display: block;
width: 100%;
margin: 12px 0;
}
}
</style>

View File

@@ -0,0 +1,51 @@
<template>
<div class="civpbkhh">
<div v-for="note in notes" class="note">
<div class="content _panel">
{{ note.text }}
</div>
<XReactionsViewer :note="note" ref="reactionsViewer"/>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import XReactionsViewer from '@/components/reactions-viewer.vue';
import * as os from '@/os';
export default defineComponent({
components: {
XReactionsViewer
},
data() {
return {
notes: [],
}
},
created() {
os.api('notes/featured').then(notes => {
this.notes = notes;
});
}
});
</script>
<style lang="scss" scoped>
.civpbkhh {
text-align: right;
> .note {
margin: 16px 0 16px auto;
> .content {
padding: 16px;
margin: 0 0 0 auto;
max-width: max-content;
border-radius: 16px;
}
}
}
</style>

View File

@@ -8,7 +8,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import XSetup from './welcome.setup.vue';
import XEntrance from './welcome.entrance.vue';
import XEntrance from './welcome.entrance.a.vue';
import { instanceName } from '@/config';
import * as os from '@/os';

View File

@@ -191,6 +191,7 @@ export default defineComponent({
} : undefined, null, {
icon: faTrashAlt,
text: this.$ts.remove,
danger: true,
action: () => {
removeColumn(this.column.id);
}

View File

@@ -45,7 +45,6 @@ export default defineComponent({
return {
deckStore,
pageInfo: null,
pageKey: 0,
}
},

View File

@@ -54,10 +54,10 @@
</template>
<script lang="ts">
import { defineComponent, defineAsyncComponent, markRaw } from 'vue';
import { defineComponent, defineAsyncComponent } from 'vue';
import { faLayerGroup, faBars, faHome, faCircle, faWindowMaximize, faColumns, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import { faBell } from '@fortawesome/free-regular-svg-icons';
import { host } from '@/config';
import { instanceName } from '@/config';
import { StickySidebar } from '@/scripts/sticky-sidebar';
import XSidebar from '@/components/sidebar.vue';
import XCommon from './_common_/common.vue';
@@ -87,8 +87,6 @@ export default defineComponent({
data() {
return {
host: host,
pageKey: 0,
pageInfo: null,
isDesktop: window.innerWidth >= DESKTOP_THRESHOLD,
menuDef: sidebarDef,
@@ -109,12 +107,6 @@ export default defineComponent({
}
},
watch: {
$route(to, from) {
this.pageKey++;
},
},
created() {
document.documentElement.style.overflowY = 'scroll';
@@ -155,6 +147,7 @@ export default defineComponent({
if (page == null) return;
if (page.INFO) {
this.pageInfo = page.INFO;
document.title = `${this.pageInfo.title} | ${instanceName}`;
}
},

View File

@@ -63,7 +63,6 @@ export default defineComponent({
return {
host,
instanceName,
pageKey: 0,
pageInfo: null,
meta: null,
narrow: window.innerWidth < 1280,
@@ -88,12 +87,6 @@ export default defineComponent({
},
},
watch: {
$route(to, from) {
this.pageKey++;
},
},
created() {
document.documentElement.style.overflowY = 'scroll';

View File

@@ -1,13 +1,13 @@
<template>
<div class="mk-app" :style="{ backgroundImage: root ? `url(${ $instance.backgroundImageUrl })` : 'none' }">
<div class="mk-app">
<a v-if="root" href="https://github.com/syuilo/misskey" target="_blank" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:var(--panel); color:var(--fg); position: fixed; z-index: 10; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>
<div class="side" v-if="!narrow">
<XKanban class="kanban" full :transparent="root" :powered-by="root"/>
<div class="side" v-if="!narrow && !root">
<XKanban class="kanban" full/>
</div>
<div class="main">
<XKanban class="banner" :full="root" :transparent="root" :powered-by="root" v-if="narrow"/>
<XKanban class="banner" :powered-by="root" v-if="narrow && !root"/>
<div class="contents">
<XHeader class="header" :info="pageInfo" v-if="!root"/>
@@ -76,7 +76,6 @@ export default defineComponent({
return {
host,
instanceName,
pageKey: 0,
pageInfo: null,
meta: null,
showMenu: false,
@@ -106,14 +105,8 @@ export default defineComponent({
},
},
watch: {
$route(to, from) {
this.pageKey++;
},
},
created() {
document.documentElement.style.overflowY = 'scroll';
//document.documentElement.style.overflowY = 'scroll';
os.api('meta', { detail: true }).then(meta => {
this.meta = meta;

View File

@@ -38,18 +38,11 @@ export default defineComponent({
data() {
return {
host: host,
pageKey: 0,
pageInfo: null,
faLayerGroup, faBars, faBell, faHome, faCircle,
};
},
watch: {
$route(to, from) {
this.pageKey++;
},
},
created() {
document.documentElement.style.overflowY = 'scroll';
},

View File

@@ -14,6 +14,7 @@ export default function(app: App) {
app.component('MkwFederation', defineAsyncComponent(() => import('./federation.vue')));
app.component('MkwPostForm', defineAsyncComponent(() => import('./post-form.vue')));
app.component('MkwSlideshow', defineAsyncComponent(() => import('./slideshow.vue')));
app.component('MkwOnlineUsers', defineAsyncComponent(() => import('./online-users.vue')));
app.component('MkwButton', defineAsyncComponent(() => import('./button.vue')));
}
@@ -31,5 +32,6 @@ export const widgets = [
'federation',
'postForm',
'slideshow',
'onlineUsers',
'button',
];

View File

@@ -0,0 +1,67 @@
<template>
<div class="mkw-onlineUsers" :class="{ _panel: !props.transparent, pad: !props.transparent }">
<I18n v-if="onlineUsersCount" :src="$ts.onlineUsersCount" text-tag="span" class="text">
<template #n><b>{{ onlineUsersCount }}</b></template>
</I18n>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import define from './define';
import * as os from '@/os';
const widget = define({
name: 'onlineUsers',
props: () => ({
transparent: {
type: 'boolean',
default: true,
},
})
});
export default defineComponent({
extends: widget,
data() {
return {
onlineUsersCount: null,
clock: null,
};
},
created() {
this.tick();
this.clock = setInterval(this.tick, 1000 * 15);
},
beforeUnmount() {
clearInterval(this.clock);
},
methods: {
tick() {
os.api('get-online-users-count').then(res => {
this.onlineUsersCount = res.count;
});
}
}
});
</script>
<style lang="scss" scoped>
.mkw-onlineUsers {
text-align: center;
&.pad {
padding: 16px 0;
}
> .text {
::v-deep(b) {
color: #41b781;
}
::v-deep(span) {
opacity: 0.7;
}
}
}
</style>

View File

@@ -0,0 +1,22 @@
import define from '../define';
import redis from '../../../db/redis';
import config from '../../../config';
export const meta = {
tags: ['meta'],
requireCredential: false as const,
params: {
}
};
export default define(meta, (ps, user) => {
return new Promise((res, rej) => {
redis.pubsub('numsub', config.host, (_, x) => {
res({
count: x[1]
});
});
});
});

154
yarn.lock
View File

@@ -1004,36 +1004,36 @@
resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
"@vue/compiler-core@3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.0.3.tgz#dbb4d5eb91f294038f0bed170a1c25f59f7dc74f"
integrity sha512-iWlRT8RYLmz7zkg84pTOriNUzjH7XACWN++ImFkskWXWeev29IKi7p76T9jKDaMZoPiGcUZ0k9wayuASWVxOwg==
"@vue/compiler-core@3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.0.5.tgz#a6e54cabe9536e74c6513acd2649f311af1d43ac"
integrity sha512-iFXwk2gmU/GGwN4hpBwDWWMLvpkIejf/AybcFtlQ5V1ur+5jwfBaV0Y1RXoR6ePfBPJixtKZ3PmN+M+HgMAtfQ==
dependencies:
"@babel/parser" "^7.12.0"
"@babel/types" "^7.12.0"
"@vue/shared" "3.0.3"
"@vue/shared" "3.0.5"
estree-walker "^2.0.1"
source-map "^0.6.1"
"@vue/compiler-dom@3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.0.3.tgz#582ba30bc82da8409868bc1153ff0e0e2be617e5"
integrity sha512-6GdUbDPjsc0MDZGAgpi4lox+d+aW9/brscwBOLOFfy9wcI9b6yLPmBbjdIsJq3pYdJWbdvACdJ77avBBdHEP8A==
"@vue/compiler-dom@3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.0.5.tgz#7885a13e6d18f64dde8ebceec052ed2c102696c2"
integrity sha512-HSOSe2XSPuCkp20h4+HXSiPH9qkhz6YbW9z9ZtL5vef2T2PMugH7/osIFVSrRZP/Ul5twFZ7MIRlp8tPX6e4/g==
dependencies:
"@vue/compiler-core" "3.0.3"
"@vue/shared" "3.0.3"
"@vue/compiler-core" "3.0.5"
"@vue/shared" "3.0.5"
"@vue/compiler-sfc@3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.0.3.tgz#7fad9d40e139dd717713c0db701e1eb776f8349f"
integrity sha512-YocHSirye85kRVC4lU0+SE6uhrwGJzbhwkrqG4g6kmsAUopZ0qUjbICMlej5bYx2+AUz9yBIM7hpK8nIKFVFjg==
"@vue/compiler-sfc@3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.0.5.tgz#3ae08e60244a72faf9598361874fb7bdb5b1d37c"
integrity sha512-uOAC4X0Gx3SQ9YvDC7YMpbDvoCmPvP0afVhJoxRotDdJ+r8VO3q4hFf/2f7U62k4Vkdftp6DVni8QixrfYzs+w==
dependencies:
"@babel/parser" "^7.12.0"
"@babel/types" "^7.12.0"
"@vue/compiler-core" "3.0.3"
"@vue/compiler-dom" "3.0.3"
"@vue/compiler-ssr" "3.0.3"
"@vue/shared" "3.0.3"
"@vue/compiler-core" "3.0.5"
"@vue/compiler-dom" "3.0.5"
"@vue/compiler-ssr" "3.0.5"
"@vue/shared" "3.0.5"
consolidate "^0.16.0"
estree-walker "^2.0.1"
hash-sum "^2.0.0"
@@ -1045,42 +1045,42 @@
postcss-selector-parser "^6.0.4"
source-map "^0.6.1"
"@vue/compiler-ssr@3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.0.3.tgz#7d9e5c1b8c71d69865ac6c48d2e6eb2eecb68501"
integrity sha512-IjJMoHCiDk939Ix7Q5wrex59TVJr6JFQ95gf36f4G4UrVau0GGY/3HudnWT/6eyWJ7267+odqQs1uCZgDfL/Ww==
"@vue/compiler-ssr@3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.0.5.tgz#7661ad891a0be948726c7f7ad1e425253c587b83"
integrity sha512-Wm//Kuxa1DpgjE4P9W0coZr8wklOfJ35Jtq61CbU+t601CpPTK4+FL2QDBItaG7aoUUDCWL5nnxMkuaOgzTBKg==
dependencies:
"@vue/compiler-dom" "3.0.3"
"@vue/shared" "3.0.3"
"@vue/compiler-dom" "3.0.5"
"@vue/shared" "3.0.5"
"@vue/reactivity@3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.0.3.tgz#681ee01ceff9219bc4da6bbb7d9c97d452e44d1d"
integrity sha512-t39Qmc42MX7wJtf8L6tHlu17eP9Rc5w4aRnxpLHNWoaRxddv/7FBhWqusJ2Bwkk8ixFHOQeejcLMt5G469WYJw==
"@vue/reactivity@3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.0.5.tgz#e3789e4d523d845f9ae0b4d770e2b45594742fd2"
integrity sha512-3xodUE3sEIJgS7ntwUbopIpzzvi7vDAOjVamfb2l+v1FUg0jpd3gf62N2wggJw3fxBMr+QvyxpD+dBoxLsmAjw==
dependencies:
"@vue/shared" "3.0.3"
"@vue/shared" "3.0.5"
"@vue/runtime-core@3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.0.3.tgz#edab3c9ad122cf8afd034b174cd20c073fbf950a"
integrity sha512-Fd1JVnYI6at0W/2ERwJuTSq4S22gNt8bKEbICcvCAac7hJUZ1rylThlrhsvrgA+DVkWU01r0niNZQ4UddlNw7g==
"@vue/runtime-core@3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.0.5.tgz#da6331d5f300d5794e9e0ebdc8a8bd72a9e19962"
integrity sha512-Cnyi2NqREwOLcTEsIi1DQX1hHtkVj4eGm4hBG7HhokS05DqpK4/80jG6PCCnCH9rIJDB2FqtaODX397210plXg==
dependencies:
"@vue/reactivity" "3.0.3"
"@vue/shared" "3.0.3"
"@vue/reactivity" "3.0.5"
"@vue/shared" "3.0.5"
"@vue/runtime-dom@3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.0.3.tgz#5e3e5e5418b9defcac988d2be0cf65596fa2cc03"
integrity sha512-ytTvSlRaEYvLQUkkpruIBizWIwuIeHER0Ch/evO6kUaPLjZjX3NerVxA40cqJx8rRjb9keQso21U2Jcpk8GsTg==
"@vue/runtime-dom@3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.0.5.tgz#1ce2c9c449e26ab06963da0064096e882a7a8935"
integrity sha512-iilX1KySeIzHHtErT6Y44db1rhWK5tAI0CiJIPr+SJoZ2jbjoOSE6ff/jfIQakchbm1d6jq6VtRVnp5xYdOXKA==
dependencies:
"@vue/runtime-core" "3.0.3"
"@vue/shared" "3.0.3"
"@vue/runtime-core" "3.0.5"
"@vue/shared" "3.0.5"
csstype "^2.6.8"
"@vue/shared@3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.0.3.tgz#ef12ebff93a446df281e8a0fd765b5aea8e7745b"
integrity sha512-yGgkF7u4W0Dmwri9XdeY50kOowN4UIX7aBQ///jbxx37itpzVjK7QzvD3ltQtPfWaJDGBfssGL0wpAgwX9OJpQ==
"@vue/shared@3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.0.5.tgz#c131d88bd6713cc4d93b3bb1372edb1983225ff0"
integrity sha512-gYsNoGkWejBxNO6SNRjOh/xKeZ0H0V+TFzaPzODfBjkAIb0aQgBuixC1brandC/CDJy1wYPwSoYrXpvul7m6yw==
"@webassemblyjs/ast@1.9.1":
version "1.9.1"
@@ -5035,10 +5035,10 @@ insert-text-at-cursor@0.3.0:
resolved "https://registry.yarnpkg.com/insert-text-at-cursor/-/insert-text-at-cursor-0.3.0.tgz#1819607680ec1570618347c4cd475e791faa25da"
integrity sha512-/nPtyeX9xPUvxZf+r0518B7uqNKlP+LqNJqSiXFEaa2T71rWIwTVXGH7hB9xO/EVdwa5/pWlFCPwShOW81XIxQ==
install-artifact-from-github@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/install-artifact-from-github/-/install-artifact-from-github-1.1.3.tgz#552f1ec3e693f970726e3f68018ff5885665ec9e"
integrity sha512-iNuncO/pI1w0UOrebs9dwwVpXqERkszPcb7AYq2hbsJDS3X+XdZ+E5kE91EBSc98mjvCMWOoBa1Zk3hVeP1ddA==
install-artifact-from-github@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/install-artifact-from-github/-/install-artifact-from-github-1.2.0.tgz#adcbd123c16a4337ec44ea76d0ebf253cc16b074"
integrity sha512-3OxCPcY55XlVM3kkfIpeCgmoSKnMsz2A3Dbhsq0RXpIknKQmrX1YiznCeW9cD2ItFmDxziA3w6Eg8d80AoL3oA==
interpret@^1.1.0:
version "1.2.0"
@@ -6579,12 +6579,7 @@ mz@^2.4.0, mz@^2.7.0:
object-assign "^4.0.1"
thenify-all "^1.0.0"
nan@^2.14.0:
version "2.14.1"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
nan@^2.14.2:
nan@^2.14.0, nan@^2.14.2:
version "2.14.2"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
@@ -8509,12 +8504,12 @@ rdf-canonize@^1.0.2:
node-forge "^0.9.1"
semver "^6.3.0"
re2@1.15.8:
version "1.15.8"
resolved "https://registry.yarnpkg.com/re2/-/re2-1.15.8.tgz#654dfbd889acc2649773a2b32dfb9feb357ca9bc"
integrity sha512-CZm4HMuNbY+LP5LjFQvBxbQmvS7iJiVR3w23Bk3jYxZFUj6wPiYRvDikyVpqHYLioVAWcgjG6F90Pk4z7ehUSg==
re2@1.15.9:
version "1.15.9"
resolved "https://registry.yarnpkg.com/re2/-/re2-1.15.9.tgz#9ed16171edcb0bc4f0e239bf55229ff3f26acbe3"
integrity sha512-AXWEhpMTBdC+3oqbjdU07dk0pBCvxh5vbOMLERL6Y8FYBSGn4vXlLe8cYszn64Yy7H8keVMrgPzoSvOd4mePpg==
dependencies:
install-artifact-from-github "^1.1.3"
install-artifact-from-github "^1.2.0"
nan "^2.14.2"
node-gyp "^7.1.2"
@@ -10025,16 +10020,11 @@ tiny-emitter@^2.0.0:
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
tinycolor2@1.4.2:
tinycolor2@1.4.2, tinycolor2@^1.1.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803"
integrity sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==
tinycolor2@^1.1.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8"
integrity sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=
tmp@0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14"
@@ -10320,10 +10310,10 @@ typeorm@0.2.29:
yargonaut "^1.1.2"
yargs "^16.0.3"
typescript@4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.2.tgz#6369ef22516fe5e10304aae5a5c4862db55380e9"
integrity sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==
typescript@4.1.3:
version "4.1.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7"
integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==
uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.6"
@@ -10633,10 +10623,10 @@ void-elements@^2.0.1:
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=
vue-color@2.7.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/vue-color/-/vue-color-2.7.1.tgz#ca035109ea0010f0d60b889b97d63d37ac712f2d"
integrity sha512-u3yl46B2eEej9zfAOIRRSphX1QfeNQzMwO82EIA+aoi0AKX3o1KcfsmMzm4BFkkj2ukCxLVfQ41k7g1gSI7SlA==
vue-color@2.8.1:
version "2.8.1"
resolved "https://registry.yarnpkg.com/vue-color/-/vue-color-2.8.1.tgz#a090f3dcf8ed6f07afdb865cac84b19a73302e70"
integrity sha512-BoLCEHisXi2QgwlhZBg9UepvzZZmi4176vbr+31Shen5WWZwSLVgdScEPcB+yrAtuHAz42309C0A4+WiL9lNBw==
dependencies:
clamp "^1.0.1"
lodash.throttle "^4.0.0"
@@ -10687,14 +10677,14 @@ vue-style-loader@4.1.2:
hash-sum "^1.0.2"
loader-utils "^1.0.2"
vue@3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.0.3.tgz#ad94a475e6ebbf3904673b6a0ae46e47b957bd72"
integrity sha512-BZG5meD5vLWdvfnRL5WqfDy+cnXO1X/SweModGUna78bdFPZW6+ZO1tU9p0acrskX3DKFcfSp2s4SZnMjABx6w==
vue@3.0.5:
version "3.0.5"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.0.5.tgz#de1b82eba24abfe71e0970fc9b8d4b2babdc3fe1"
integrity sha512-TfaprOmtsAfhQau7WsomXZ8d9op/dkQLNIq8qPV3A0Vxs6GR5E+c1rfJS1SDkXRQj+dFyfnec7+U0Be1huiScg==
dependencies:
"@vue/compiler-dom" "3.0.3"
"@vue/runtime-dom" "3.0.3"
"@vue/shared" "3.0.3"
"@vue/compiler-dom" "3.0.5"
"@vue/runtime-dom" "3.0.5"
"@vue/shared" "3.0.5"
vuedraggable@4.0.1:
version "4.0.1"