Merge branch 'develop' into swn
This commit is contained in:
commit
92c3162506
86 changed files with 1610 additions and 1000 deletions
|
@ -13,9 +13,16 @@
|
|||
## 12.x.x (unreleased)
|
||||
|
||||
### Improvements
|
||||
- アカウント登録にメールアドレスの設定を必須にするオプション
|
||||
- クライアント: アニメーションを減らす設定をメニューのアニメーションにも適用するように
|
||||
- クライアント: MFM関数構文のサジェストを実装
|
||||
- クライアント: 未読の通知のみ表示する機能
|
||||
- クライアント: 通知ページで通知の種類によるフィルタ
|
||||
- ActivityPub: HTML -> MFMの変換を強化
|
||||
- API: i/notifications に unreadOnly オプションを追加
|
||||
- API: ap系のエンドポイントをログイン必須化+レートリミット追加
|
||||
- Misskeyのコマンドラインオプションを廃止
|
||||
- 代わりに環境変数で設定することができます
|
||||
|
||||
### Bugfixes
|
||||
- Fix createDeleteAccountJob
|
||||
|
|
|
@ -791,6 +791,14 @@ resolved: "解決済み"
|
|||
unresolved: "未解決"
|
||||
itsOn: "オンになっています"
|
||||
itsOff: "オフになっています"
|
||||
emailRequiredForSignup: "アカウント登録にメールアドレスを必須にする"
|
||||
unread: "未読"
|
||||
filter: "フィルタ"
|
||||
|
||||
_signup:
|
||||
almostThere: "ほとんど完了です"
|
||||
emailAddressInfo: "あなたが使っているメールアドレスを入力してください。"
|
||||
emailSent: "入力されたメールアドレス({email})宛に確認のメールが送信されました。メールに記載されたリンクにアクセスすると、アカウントの作成が完了します。"
|
||||
|
||||
_accountDelete:
|
||||
accountDelete: "アカウントの削除"
|
||||
|
|
14
migration/1633068642000-email-required-for-signup.ts
Normal file
14
migration/1633068642000-email-required-for-signup.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class emailRequiredForSignup1633068642000 implements MigrationInterface {
|
||||
name = 'emailRequiredForSignup1633068642000'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "emailRequiredForSignup" boolean NOT NULL DEFAULT false`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "emailRequiredForSignup"`);
|
||||
}
|
||||
|
||||
}
|
16
migration/1633071909016-user-pending.ts
Normal file
16
migration/1633071909016-user-pending.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class userPending1633071909016 implements MigrationInterface {
|
||||
name = 'userPending1633071909016'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`CREATE TABLE "user_pending" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "code" character varying(128) NOT NULL, "username" character varying(128) NOT NULL, "email" character varying(128) NOT NULL, "password" character varying(128) NOT NULL, CONSTRAINT "PK_d4c84e013c98ec02d19b8fbbafa" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_4e5c4c99175638ec0761714ab0" ON "user_pending" ("code") `);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP INDEX "IDX_4e5c4c99175638ec0761714ab0"`);
|
||||
await queryRunner.query(`DROP TABLE "user_pending"`);
|
||||
}
|
||||
|
||||
}
|
49
package.json
49
package.json
|
@ -47,7 +47,7 @@
|
|||
"@sinonjs/fake-timers": "7.1.2",
|
||||
"@syuilo/aiscript": "0.11.1",
|
||||
"@types/bcryptjs": "2.4.2",
|
||||
"@types/bull": "3.15.4",
|
||||
"@types/bull": "3.15.5",
|
||||
"@types/cbor": "6.0.0",
|
||||
"@types/dateformat": "3.0.1",
|
||||
"@types/escape-regexp": "0.0.0",
|
||||
|
@ -63,17 +63,17 @@
|
|||
"@types/koa-bodyparser": "4.3.3",
|
||||
"@types/koa-cors": "0.0.2",
|
||||
"@types/koa-favicon": "2.0.21",
|
||||
"@types/koa-logger": "3.1.1",
|
||||
"@types/koa-logger": "3.1.2",
|
||||
"@types/koa-mount": "4.0.1",
|
||||
"@types/koa-send": "4.1.3",
|
||||
"@types/koa-views": "7.0.0",
|
||||
"@types/koa__cors": "3.0.3",
|
||||
"@types/koa__multer": "2.0.3",
|
||||
"@types/koa__router": "8.0.8",
|
||||
"@types/markdown-it": "12.2.1",
|
||||
"@types/markdown-it": "12.2.3",
|
||||
"@types/matter-js": "0.17.5",
|
||||
"@types/mocha": "8.2.3",
|
||||
"@types/node": "16.9.6",
|
||||
"@types/node": "16.10.3",
|
||||
"@types/node-fetch": "2.5.12",
|
||||
"@types/nodemailer": "6.4.4",
|
||||
"@types/nprogress": "0.2.0",
|
||||
|
@ -102,28 +102,27 @@
|
|||
"@types/webpack": "5.28.0",
|
||||
"@types/webpack-stream": "3.2.12",
|
||||
"@types/websocket": "1.0.4",
|
||||
"@types/ws": "7.4.7",
|
||||
"@typescript-eslint/parser": "4.31.2",
|
||||
"@types/ws": "8.2.0",
|
||||
"@typescript-eslint/parser": "4.33.0",
|
||||
"@vue/compiler-sfc": "3.2.19",
|
||||
"abort-controller": "3.0.0",
|
||||
"apexcharts": "3.28.3",
|
||||
"autobind-decorator": "2.4.0",
|
||||
"autosize": "4.0.4",
|
||||
"autwh": "0.1.0",
|
||||
"aws-sdk": "2.992.0",
|
||||
"aws-sdk": "2.1003.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"blurhash": "1.1.4",
|
||||
"broadcast-channel": "4.2.0",
|
||||
"bull": "3.29.2",
|
||||
"cacheable-lookup": "6.0.1",
|
||||
"cacheable-lookup": "6.0.2",
|
||||
"cafy": "15.2.1",
|
||||
"cbor": "8.0.0",
|
||||
"cbor": "8.0.2",
|
||||
"chalk": "4.1.2",
|
||||
"chart.js": "2.9.4",
|
||||
"cli-highlight": "2.1.11",
|
||||
"commander": "8.1.0",
|
||||
"compare-versions": "3.6.0",
|
||||
"concurrently": "6.2.1",
|
||||
"concurrently": "6.3.0",
|
||||
"content-disposition": "0.5.3",
|
||||
"crc-32": "1.2.0",
|
||||
"css-loader": "6.3.0",
|
||||
|
@ -131,18 +130,18 @@
|
|||
"dateformat": "4.5.1",
|
||||
"escape-regexp": "0.0.1",
|
||||
"eslint": "7.32.0",
|
||||
"eslint-plugin-vue": "7.18.0",
|
||||
"eslint-plugin-vue": "7.19.1",
|
||||
"eventemitter3": "4.0.7",
|
||||
"feed": "4.2.2",
|
||||
"file-type": "16.5.3",
|
||||
"fluent-ffmpeg": "2.1.2",
|
||||
"glob": "7.1.7",
|
||||
"glob": "7.2.0",
|
||||
"got": "11.8.2",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-cssnano": "2.1.3",
|
||||
"gulp-rename": "2.0.0",
|
||||
"gulp-replace": "1.1.3",
|
||||
"gulp-terser": "2.0.1",
|
||||
"gulp-terser": "2.1.0",
|
||||
"gulp-tslint": "8.1.4",
|
||||
"hpagent": "0.1.2",
|
||||
"http-signature": "1.3.5",
|
||||
|
@ -157,7 +156,7 @@
|
|||
"jsonld": "5.2.0",
|
||||
"jsrsasign": "8.0.20",
|
||||
"katex": "0.13.18",
|
||||
"koa": "2.13.1",
|
||||
"koa": "2.13.3",
|
||||
"koa-bodyparser": "4.3.0",
|
||||
"koa-favicon": "2.1.0",
|
||||
"koa-json-body": "5.3.0",
|
||||
|
@ -170,19 +169,19 @@
|
|||
"markdown-it": "12.2.0",
|
||||
"markdown-it-anchor": "7.1.0",
|
||||
"matter-js": "0.17.1",
|
||||
"mfm-js": "0.19.0",
|
||||
"mfm-js": "0.20.0",
|
||||
"misskey-js": "0.0.6",
|
||||
"mocha": "8.4.0",
|
||||
"ms": "2.1.3",
|
||||
"multer": "1.4.3",
|
||||
"nested-property": "4.0.0",
|
||||
"node-fetch": "2.6.1",
|
||||
"nodemailer": "6.6.3",
|
||||
"nodemailer": "6.6.5",
|
||||
"os-utils": "0.0.14",
|
||||
"parse5": "6.0.1",
|
||||
"pg": "8.7.1",
|
||||
"portscanner": "2.2.0",
|
||||
"postcss": "8.3.7",
|
||||
"postcss": "8.3.9",
|
||||
"postcss-loader": "6.1.1",
|
||||
"prismjs": "1.25.0",
|
||||
"private-ip": "2.2.1",
|
||||
|
@ -214,7 +213,7 @@
|
|||
"style-loader": "3.3.0",
|
||||
"summaly": "2.4.1",
|
||||
"syslog-pro": "1.0.0",
|
||||
"systeminformation": "5.9.3",
|
||||
"systeminformation": "5.9.4",
|
||||
"syuilo-password-strength": "0.0.1",
|
||||
"textarea-caret": "3.1.0",
|
||||
"three": "0.117.1",
|
||||
|
@ -223,12 +222,12 @@
|
|||
"tmp": "0.2.1",
|
||||
"ts-loader": "9.2.6",
|
||||
"ts-node": "10.2.1",
|
||||
"tsc-alias": "1.3.9",
|
||||
"tsc-alias": "1.3.10",
|
||||
"tsconfig-paths": "3.11.0",
|
||||
"tslint": "6.1.3",
|
||||
"tslint-sonarts": "1.9.0",
|
||||
"twemoji-parser": "13.1.0",
|
||||
"typeorm": "0.2.37",
|
||||
"typeorm": "0.2.38",
|
||||
"typescript": "4.4.3",
|
||||
"ulid": "2.3.0",
|
||||
"uuid": "8.3.2",
|
||||
|
@ -242,17 +241,17 @@
|
|||
"vue-svg-loader": "0.17.0-beta.2",
|
||||
"vuedraggable": "4.0.1",
|
||||
"web-push": "3.4.5",
|
||||
"webpack": "5.54.0",
|
||||
"webpack-cli": "4.8.0",
|
||||
"webpack": "5.58.0",
|
||||
"webpack-cli": "4.9.0",
|
||||
"websocket": "1.0.34",
|
||||
"ws": "8.2.2",
|
||||
"ws": "8.2.3",
|
||||
"xev": "2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@redocly/openapi-core": "1.0.0-beta.54",
|
||||
"@types/fluent-ffmpeg": "2.1.17",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "8.4.1",
|
||||
"cypress": "8.5.0",
|
||||
"start-server-and-test": "1.14.0"
|
||||
}
|
||||
}
|
||||
|
|
23
src/argv.ts
23
src/argv.ts
|
@ -1,23 +0,0 @@
|
|||
import { Command } from 'commander';
|
||||
import config from '@/config/index';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program.version(config.version);
|
||||
program.option('--no-daemons', 'Disable daemon processes (for debbuging)');
|
||||
program.option('--disable-clustering', 'Disable clustering');
|
||||
program.option('--only-server', 'Run server only (without job queue processing)');
|
||||
program.option('--only-queue', 'Pocessing job queue only (without server)');
|
||||
program.option('--quiet', 'Suppress all logs');
|
||||
program.option('--verbose', 'Enable all logs');
|
||||
program.option('--with-log-time', 'Include timestamp for each logs');
|
||||
program.option('--slow', 'Delay all requests (for debbuging)');
|
||||
program.option('--color', 'This option is a dummy for some external program\'s (e.g. forever) issue.');
|
||||
program.parse(process.argv);
|
||||
|
||||
if (process.env.MK_ONLY_QUEUE) program.onlyQueue = true;
|
||||
if (process.env.NODE_ENV === 'test') program.disableClustering = true;
|
||||
//if (process.env.NODE_ENV === 'test') program.quiet = true;
|
||||
if (process.env.NODE_ENV === 'test') program.noDaemons = true;
|
||||
|
||||
export { program };
|
|
@ -3,7 +3,7 @@ import * as chalk from 'chalk';
|
|||
import Xev from 'xev';
|
||||
|
||||
import Logger from '@/services/logger';
|
||||
import { program } from '../argv';
|
||||
import { envOption } from '../env';
|
||||
|
||||
// for typeorm
|
||||
import 'reflect-metadata';
|
||||
|
@ -20,7 +20,7 @@ const ev = new Xev();
|
|||
export default async function() {
|
||||
process.title = `Misskey (${cluster.isMaster ? 'master' : 'worker'})`;
|
||||
|
||||
if (cluster.isMaster || program.disableClustering) {
|
||||
if (cluster.isMaster || envOption.disableClustering) {
|
||||
await masterMain();
|
||||
|
||||
if (cluster.isMaster) {
|
||||
|
@ -28,7 +28,7 @@ export default async function() {
|
|||
}
|
||||
}
|
||||
|
||||
if (cluster.isWorker || program.disableClustering) {
|
||||
if (cluster.isWorker || envOption.disableClustering) {
|
||||
await workerMain();
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ cluster.on('exit', worker => {
|
|||
});
|
||||
|
||||
// Display detail of unhandled promise rejection
|
||||
if (!program.quiet) {
|
||||
if (!envOption.quiet) {
|
||||
process.on('unhandledRejection', console.dir);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import Logger from '@/services/logger';
|
|||
import loadConfig from '@/config/load';
|
||||
import { Config } from '@/config/types';
|
||||
import { lessThan } from '@/prelude/array';
|
||||
import { program } from '../argv';
|
||||
import { envOption } from '../env';
|
||||
import { showMachineInfo } from '@/misc/show-machine-info';
|
||||
import { initDb } from '../db/postgre';
|
||||
|
||||
|
@ -25,7 +25,7 @@ const logger = new Logger('core', 'cyan');
|
|||
const bootLogger = logger.createSubLogger('boot', 'magenta', false);
|
||||
|
||||
function greet() {
|
||||
if (!program.quiet) {
|
||||
if (!envOption.quiet) {
|
||||
//#region Misskey logo
|
||||
const v = `v${meta.version}`;
|
||||
console.log(' _____ _ _ ');
|
||||
|
@ -73,13 +73,13 @@ export async function masterMain() {
|
|||
|
||||
bootLogger.succ('Misskey initialized');
|
||||
|
||||
if (!program.disableClustering) {
|
||||
if (!envOption.disableClustering) {
|
||||
await spawnWorkers(config.clusterLimit);
|
||||
}
|
||||
|
||||
bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, null, true);
|
||||
|
||||
if (!program.noDaemons) {
|
||||
if (!envOption.noDaemons) {
|
||||
require('../daemons/server-stats').default();
|
||||
require('../daemons/queue-stats').default();
|
||||
require('../daemons/janitor').default();
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
<template>
|
||||
<div class="fdidabkb" :class="{ slim: titleOnly || narrow }" :style="`--height:${height};`" :key="key">
|
||||
<transition :name="$store.state.animation ? 'header' : ''" mode="out-in" appear>
|
||||
<div class="buttons left" v-if="backButton">
|
||||
<button class="_button button back" @click.stop="$emit('back')" @touchstart="preventDrag" v-tooltip="$ts.goBack"><i class="fas fa-chevron-left"></i></button>
|
||||
</div>
|
||||
</transition>
|
||||
<div class="fdidabkb" :class="{ slim: narrow, thin }" :style="{ background: bg }" @click="onClick">
|
||||
<template v-if="info">
|
||||
<div class="titleContainer" @click="showTabsPopup">
|
||||
<i v-if="info.icon" class="icon" :class="info.icon"></i>
|
||||
|
@ -31,20 +26,29 @@
|
|||
</template>
|
||||
<div class="buttons right">
|
||||
<template v-if="info && info.actions && !narrow">
|
||||
<button v-for="action in info.actions" class="_button button" :class="{ highlighted: action.highlighted }" @click.stop="action.handler" @touchstart="preventDrag" v-tooltip="action.text"><i :class="action.icon"></i></button>
|
||||
<template v-for="action in info.actions">
|
||||
<MkButton class="fullButton" v-if="action.asFullButton" @click.stop="action.handler" primary><i :class="action.icon" style="margin-right: 6px;"></i>{{ action.text }}</MkButton>
|
||||
<button v-else class="_button button" :class="{ highlighted: action.highlighted }" @click.stop="action.handler" @touchstart="preventDrag" v-tooltip="action.text"><i :class="action.icon"></i></button>
|
||||
</template>
|
||||
</template>
|
||||
<button v-if="shouldShowMenu" class="_button button" @click.stop="showMenu" @touchstart="preventDrag" v-tooltip="$ts.menu"><i class="fas fa-ellipsis-h"></i></button>
|
||||
<button v-if="closeButton" class="_button button" @click.stop="$emit('close')" @touchstart="preventDrag" v-tooltip="$ts.close"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import * as tinycolor from 'tinycolor2';
|
||||
import { popupMenu } from '@client/os';
|
||||
import { url } from '@client/config';
|
||||
import { scrollToTop } from '@client/scripts/scroll';
|
||||
import MkButton from '@client/components/ui/button.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkButton
|
||||
},
|
||||
|
||||
props: {
|
||||
info: {
|
||||
required: true
|
||||
|
@ -52,28 +56,17 @@ export default defineComponent({
|
|||
menu: {
|
||||
required: false
|
||||
},
|
||||
backButton: {
|
||||
type: Boolean,
|
||||
thin: {
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
closeButton: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
titleOnly: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
bg: null,
|
||||
narrow: false,
|
||||
height: 0,
|
||||
key: 0,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -92,19 +85,21 @@ export default defineComponent({
|
|||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
info() {
|
||||
this.key++;
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.height = this.$el.parentElement.offsetHeight + 'px';
|
||||
this.narrow = this.titleOnly || this.$el.parentElement.offsetWidth < 500;
|
||||
new ResizeObserver((entries, observer) => {
|
||||
this.height = this.$el.parentElement.offsetHeight + 'px';
|
||||
this.narrow = this.titleOnly || this.$el.parentElement.offsetWidth < 500;
|
||||
}).observe(this.$el);
|
||||
const rawBg = this.info?.bg || 'var(--bg)';
|
||||
const bg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
|
||||
bg.setAlpha(0.85);
|
||||
this.bg = bg.toRgbString();
|
||||
|
||||
if (this.$el.parentElement) {
|
||||
this.narrow = this.$el.parentElement.offsetWidth < 500;
|
||||
new ResizeObserver((entries, observer) => {
|
||||
this.narrow = this.$el.parentElement.offsetWidth < 500;
|
||||
}).observe(this.$el.parentElement);
|
||||
const currentStickyTop = getComputedStyle(this.$el).getPropertyValue('--stickyTop') || '0px';
|
||||
this.$el.style.setProperty('--stickyTop', currentStickyTop);
|
||||
this.$el.parentElement.style.setProperty('--stickyTop', `calc(${currentStickyTop} + ${this.$el.offsetHeight}px)`);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -154,6 +149,10 @@ export default defineComponent({
|
|||
|
||||
preventDrag(ev) {
|
||||
ev.stopPropagation();
|
||||
},
|
||||
|
||||
onClick(ev) {
|
||||
scrollToTop(this.$el, { behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -161,7 +160,18 @@ export default defineComponent({
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.fdidabkb {
|
||||
--height: 60px;
|
||||
display: flex;
|
||||
position: sticky;
|
||||
top: var(--stickyTop, 0);
|
||||
z-index: 1000;
|
||||
width: 100%;
|
||||
-webkit-backdrop-filter: var(--blur, blur(15px));
|
||||
backdrop-filter: var(--blur, blur(15px));
|
||||
|
||||
&.thin {
|
||||
--height: 50px;
|
||||
}
|
||||
|
||||
&.slim {
|
||||
text-align: center;
|
||||
|
@ -210,6 +220,12 @@ export default defineComponent({
|
|||
color: var(--accent);
|
||||
}
|
||||
}
|
||||
|
||||
> .fullButton {
|
||||
& + .fullButton {
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .titleContainer {
|
||||
|
@ -220,6 +236,7 @@ export default defineComponent({
|
|||
text-align: left;
|
||||
font-weight: bold;
|
||||
flex-shrink: 0;
|
||||
margin-left: 24px;
|
||||
|
||||
> .avatar {
|
||||
$size: 32px;
|
|
@ -13,6 +13,7 @@ import i18n from './global/i18n';
|
|||
import loading from './global/loading.vue';
|
||||
import error from './global/error.vue';
|
||||
import ad from './global/ad.vue';
|
||||
import header from './global/header.vue';
|
||||
|
||||
export default function(app: App) {
|
||||
app.component('I18n', i18n);
|
||||
|
@ -28,4 +29,5 @@ export default function(app: App) {
|
|||
app.component('MkLoading', loading);
|
||||
app.component('MkError', error);
|
||||
app.component('MkAd', ad);
|
||||
app.component('MkHeader', header);
|
||||
}
|
||||
|
|
|
@ -2,9 +2,13 @@
|
|||
<MkModal ref="modal" @click="$emit('click')" @closed="$emit('closed')">
|
||||
<div class="hrmcaedk _window _narrow_" :style="{ width: `${width}px`, height: (height ? `min(${height}px, 100%)` : '100%') }">
|
||||
<div class="header" @contextmenu="onContextmenu">
|
||||
<span class="title">
|
||||
<XHeader :info="pageInfo" :back-button="history.length > 0" @back="back()" :close-button="true" @close="$refs.modal.close()"/>
|
||||
<button v-if="history.length > 0" class="_button" @click="back()"><i class="fas fa-arrow-left"></i></button>
|
||||
<span v-else style="display: inline-block; width: 20px"></span>
|
||||
<span v-if="pageInfo" class="title">
|
||||
<i v-if="pageInfo.icon" class="icon" :class="pageInfo.icon"></i>
|
||||
<span>{{ pageInfo.title }}</span>
|
||||
</span>
|
||||
<button class="_button" @click="$refs.modal.close()"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
<div class="body _flat_">
|
||||
<keep-alive>
|
||||
|
@ -18,7 +22,6 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import MkModal from '@client/components/ui/modal.vue';
|
||||
import XHeader from '@client/ui/_common_/header.vue';
|
||||
import { popout } from '@client/scripts/popout';
|
||||
import copyToClipboard from '@client/scripts/copy-to-clipboard';
|
||||
import { resolve } from '@client/router';
|
||||
|
@ -29,7 +32,6 @@ import * as os from '@client/os';
|
|||
export default defineComponent({
|
||||
components: {
|
||||
MkModal,
|
||||
XHeader,
|
||||
},
|
||||
|
||||
inject: {
|
||||
|
@ -172,19 +174,39 @@ export default defineComponent({
|
|||
$height-narrow: 42px;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
height: $height;
|
||||
line-height: $height;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
box-shadow: 0px 1px var(--divider);
|
||||
|
||||
> button {
|
||||
height: $height;
|
||||
width: $height;
|
||||
|
||||
&:hover {
|
||||
color: var(--fgHighlighted);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
height: $height-narrow;
|
||||
line-height: $height-narrow;
|
||||
padding-left: 16px;
|
||||
|
||||
> button {
|
||||
height: $height-narrow;
|
||||
width: $height-narrow;
|
||||
}
|
||||
}
|
||||
|
||||
> .title {
|
||||
flex: 1;
|
||||
height: $height;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
height: $height-narrow;
|
||||
padding-left: 16px;
|
||||
> .icon {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import XModalWindow from '@client/components/ui/modal-window.vue';
|
|||
import MkSwitch from './form/switch.vue';
|
||||
import MkInfo from './ui/info.vue';
|
||||
import MkButton from './ui/button.vue';
|
||||
import { notificationTypes } from '../../types';
|
||||
import { notificationTypes } from '@/types';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
|
|
@ -27,7 +27,7 @@ import { markNotificationRead } from '@client/scripts/mark-notification-read';
|
|||
import XNotification from './notification.vue';
|
||||
import XList from './date-separated-list.vue';
|
||||
import XNote from './note.vue';
|
||||
import { notificationTypes } from '../../types';
|
||||
import { notificationTypes } from '@/types';
|
||||
import * as os from '@client/os';
|
||||
import MkButton from '@client/components/ui/button.vue';
|
||||
|
||||
|
@ -49,6 +49,11 @@ export default defineComponent({
|
|||
required: false,
|
||||
default: null,
|
||||
},
|
||||
unreadOnly: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
|
@ -59,6 +64,7 @@ export default defineComponent({
|
|||
limit: 10,
|
||||
params: () => ({
|
||||
includeTypes: this.allIncludeTypes || undefined,
|
||||
unreadOnly: this.unreadOnly,
|
||||
})
|
||||
},
|
||||
};
|
||||
|
@ -77,6 +83,11 @@ export default defineComponent({
|
|||
},
|
||||
deep: true
|
||||
},
|
||||
unreadOnly: {
|
||||
handler() {
|
||||
this.reload();
|
||||
},
|
||||
},
|
||||
// TODO: vue/vuexのバグか仕様かは不明なものの、プロフィール更新するなどして $i が更新されると、
|
||||
// mutingNotificationTypes に変化が無くてもこのハンドラーが呼び出され無駄なリロードが発生するのを直す
|
||||
'$i.mutingNotificationTypes': {
|
||||
|
|
|
@ -3,12 +3,18 @@
|
|||
:initial-width="500"
|
||||
:initial-height="500"
|
||||
:can-resize="true"
|
||||
:close-button="false"
|
||||
:close-button="true"
|
||||
:contextmenu="contextmenu"
|
||||
@closed="$emit('closed')"
|
||||
>
|
||||
<template #header>
|
||||
<XHeader :info="pageInfo" :back-button="history.length > 0" @back="back()" :close-button="true" @close="close()" :title-only="true"/>
|
||||
<template v-if="pageInfo">
|
||||
<i v-if="pageInfo.icon" class="icon" :class="pageInfo.icon" style="margin-right: 0.5em;"></i>
|
||||
<span>{{ pageInfo.title }}</span>
|
||||
</template>
|
||||
</template>
|
||||
<template #headerLeft>
|
||||
<button v-if="history.length > 0" class="_button" @click="back()"><i class="fas fa-arrow-left"></i></button>
|
||||
</template>
|
||||
<div class="yrolvcoq _flat_">
|
||||
<component :is="component" v-bind="props" :ref="changePage"/>
|
||||
|
@ -19,7 +25,6 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import XWindow from '@client/components/ui/window.vue';
|
||||
import XHeader from '@client/ui/_common_/header.vue';
|
||||
import { popout } from '@client/scripts/popout';
|
||||
import copyToClipboard from '@client/scripts/copy-to-clipboard';
|
||||
import { resolve } from '@client/router';
|
||||
|
@ -29,7 +34,6 @@ import * as symbols from '@client/symbols';
|
|||
export default defineComponent({
|
||||
components: {
|
||||
XWindow,
|
||||
XHeader,
|
||||
},
|
||||
|
||||
inject: {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
<div class="_monolithic_">
|
||||
<div class="_section">
|
||||
<XSignup :auto-set="autoSet" @signup="onSignup"/>
|
||||
<XSignup :auto-set="autoSet" @signup="onSignup" @signupEmailPending="onSignupEmailPending"/>
|
||||
</div>
|
||||
</div>
|
||||
</XModalWindow>
|
||||
|
@ -40,6 +40,10 @@ export default defineComponent({
|
|||
onSignup(res) {
|
||||
this.$emit('done', res);
|
||||
this.$refs.dialog.close();
|
||||
},
|
||||
|
||||
onSignupEmailPending() {
|
||||
this.$refs.dialog.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -10,13 +10,23 @@
|
|||
<template #prefix>@</template>
|
||||
<template #suffix>@{{ host }}</template>
|
||||
<template #caption>
|
||||
<span v-if="usernameState == 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse fa-fw"></i> {{ $ts.checking }}</span>
|
||||
<span v-if="usernameState == 'ok'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.available }}</span>
|
||||
<span v-if="usernameState == 'unavailable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.unavailable }}</span>
|
||||
<span v-if="usernameState == 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.error }}</span>
|
||||
<span v-if="usernameState == 'invalid-format'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.usernameInvalidFormat }}</span>
|
||||
<span v-if="usernameState == 'min-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooShort }}</span>
|
||||
<span v-if="usernameState == 'max-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooLong }}</span>
|
||||
<span v-if="usernameState === 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse fa-fw"></i> {{ $ts.checking }}</span>
|
||||
<span v-else-if="usernameState === 'ok'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.available }}</span>
|
||||
<span v-else-if="usernameState === 'unavailable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.unavailable }}</span>
|
||||
<span v-else-if="usernameState === 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.error }}</span>
|
||||
<span v-else-if="usernameState === 'invalid-format'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.usernameInvalidFormat }}</span>
|
||||
<span v-else-if="usernameState === 'min-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooShort }}</span>
|
||||
<span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooLong }}</span>
|
||||
</template>
|
||||
</MkInput>
|
||||
<MkInput v-if="meta.emailRequiredForSignup" class="_formBlock" v-model="email" type="email" :autocomplete="Math.random()" spellcheck="false" required @update:modelValue="onChangeEmail" data-cy-signup-email>
|
||||
<template #label>{{ $ts.emailAddress }} <div class="_button _help" v-tooltip:dialog="$ts._signup.emailAddressInfo"><i class="far fa-question-circle"></i></div></template>
|
||||
<template #prefix><i class="fas fa-envelope"></i></template>
|
||||
<template #caption>
|
||||
<span v-if="emailState === 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse fa-fw"></i> {{ $ts.checking }}</span>
|
||||
<span v-else-if="emailState === 'ok'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.available }}</span>
|
||||
<span v-else-if="emailState === 'unavailable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.unavailable }}</span>
|
||||
<span v-else-if="emailState === 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.error }}</span>
|
||||
</template>
|
||||
</MkInput>
|
||||
<MkInput class="_formBlock" v-model="password" type="password" :autocomplete="Math.random()" required @update:modelValue="onChangePassword" data-cy-signup-password>
|
||||
|
@ -87,8 +97,10 @@ export default defineComponent({
|
|||
password: '',
|
||||
retypedPassword: '',
|
||||
invitationCode: '',
|
||||
email: '',
|
||||
url,
|
||||
usernameState: null,
|
||||
emailState: null,
|
||||
passwordStrength: '',
|
||||
passwordRetypeState: null,
|
||||
submitting: false,
|
||||
|
@ -148,6 +160,23 @@ export default defineComponent({
|
|||
});
|
||||
},
|
||||
|
||||
onChangeEmail() {
|
||||
if (this.email == '') {
|
||||
this.emailState = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.emailState = 'wait';
|
||||
|
||||
os.api('email-address/available', {
|
||||
emailAddress: this.email
|
||||
}).then(result => {
|
||||
this.emailState = result.available ? 'ok' : 'unavailable';
|
||||
}).catch(err => {
|
||||
this.emailState = 'error';
|
||||
});
|
||||
},
|
||||
|
||||
onChangePassword() {
|
||||
if (this.password == '') {
|
||||
this.passwordStrength = '';
|
||||
|
@ -174,20 +203,30 @@ export default defineComponent({
|
|||
os.api('signup', {
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
emailAddress: this.email,
|
||||
invitationCode: this.invitationCode,
|
||||
'hcaptcha-response': this.hCaptchaResponse,
|
||||
'g-recaptcha-response': this.reCaptchaResponse,
|
||||
}).then(() => {
|
||||
return os.api('signin', {
|
||||
username: this.username,
|
||||
password: this.password
|
||||
}).then(res => {
|
||||
this.$emit('signup', res);
|
||||
if (this.meta.emailRequiredForSignup) {
|
||||
os.dialog({
|
||||
type: 'success',
|
||||
title: this.$ts._signup.almostThere,
|
||||
text: this.$t('_signup.emailSent', { email: this.email }),
|
||||
});
|
||||
this.$emit('signupEmailPending');
|
||||
} else {
|
||||
os.api('signin', {
|
||||
username: this.username,
|
||||
password: this.password
|
||||
}).then(res => {
|
||||
this.$emit('signup', res);
|
||||
|
||||
if (this.autoSet) {
|
||||
return login(res.i);
|
||||
}
|
||||
});
|
||||
if (this.autoSet) {
|
||||
login(res.i);
|
||||
}
|
||||
});
|
||||
}
|
||||
}).catch(() => {
|
||||
this.submitting = false;
|
||||
this.$refs.hcaptcha?.reset?.();
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<MkAvatar :user="item.user" class="avatar"/><MkUserName :user="item.user"/>
|
||||
<span v-if="item.indicate" class="indicator"><i class="fas fa-circle"></i></span>
|
||||
</button>
|
||||
<button v-else @click="clicked(item.action, $event)" :tabindex="i" class="_button item" :class="{ danger: item.danger }">
|
||||
<button v-else @click="clicked(item.action, $event)" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active }" :disabled="item.active">
|
||||
<i v-if="item.icon" class="fa-fw" :class="item.icon"></i>
|
||||
<MkAvatar v-if="item.avatar" :user="item.avatar" class="avatar"/>
|
||||
<span>{{ item.text }}</span>
|
||||
|
@ -170,9 +170,14 @@ export default defineComponent({
|
|||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
width: calc(100% - 16px);
|
||||
//width: calc(100% - 16px);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 6px;
|
||||
//border-radius: 6px;
|
||||
}
|
||||
|
||||
> * {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&.danger {
|
||||
|
@ -195,7 +200,16 @@ export default defineComponent({
|
|||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&.active {
|
||||
color: var(--fgOnAccent);
|
||||
opacity: 1;
|
||||
|
||||
&:before {
|
||||
background: var(--accent);
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:disabled):hover {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
|
||||
|
@ -204,11 +218,6 @@ export default defineComponent({
|
|||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
//color: var(--fgOnAccent);
|
||||
//background: var(--accentDarken);
|
||||
}
|
||||
|
||||
&:not(:active):focus-visible {
|
||||
box-shadow: 0 0 0 2px var(--focus) inset;
|
||||
}
|
||||
|
|
|
@ -3,11 +3,16 @@
|
|||
<div class="ebkgocck" :class="{ front }" v-if="showing">
|
||||
<div class="body _window _shadow _narrow_" @mousedown="onBodyMousedown" @keydown="onKeydown">
|
||||
<div class="header" :class="{ mini }" @contextmenu.prevent.stop="onContextmenu">
|
||||
<button v-if="closeButton" class="_button" @click="close()"><i class="fas fa-times"></i></button>
|
||||
|
||||
<span class="left">
|
||||
<slot name="headerLeft"></slot>
|
||||
</span>
|
||||
<span class="title" @mousedown.prevent="onHeaderMousedown" @touchstart.prevent="onHeaderMousedown">
|
||||
<slot name="header"></slot>
|
||||
</span>
|
||||
<span class="right">
|
||||
<slot name="headerRight"></slot>
|
||||
<button v-if="closeButton" class="_button" @click="close()"><i class="fas fa-times"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="body" v-if="padding">
|
||||
<div class="_section">
|
||||
|
@ -418,12 +423,14 @@ export default defineComponent({
|
|||
height: var(--height);
|
||||
border-bottom: solid 1px var(--divider);
|
||||
|
||||
> ::v-deep(button) {
|
||||
height: var(--height);
|
||||
width: var(--height);
|
||||
> .left, > .right {
|
||||
> ::v-deep(button) {
|
||||
height: var(--height);
|
||||
width: var(--height);
|
||||
|
||||
&:hover {
|
||||
color: var(--fgHighlighted);
|
||||
&:hover {
|
||||
color: var(--fgHighlighted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
34
src/client/directives/get-size.ts
Normal file
34
src/client/directives/get-size.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { Directive } from 'vue';
|
||||
|
||||
export default {
|
||||
mounted(src, binding, vn) {
|
||||
const calc = () => {
|
||||
const height = src.clientHeight;
|
||||
const width = src.clientWidth;
|
||||
|
||||
// 要素が(一時的に)DOMに存在しないときは計算スキップ
|
||||
if (height === 0) return;
|
||||
|
||||
binding.value(width, height);
|
||||
};
|
||||
|
||||
calc();
|
||||
|
||||
// Vue3では使えなくなった
|
||||
// 無くても大丈夫か...?
|
||||
// TODO: ↑大丈夫じゃなかったので解決策を探す
|
||||
//vn.context.$on('hook:activated', calc);
|
||||
|
||||
const ro = new ResizeObserver((entries, observer) => {
|
||||
calc();
|
||||
});
|
||||
ro.observe(src);
|
||||
|
||||
src._get_size_ro_ = ro;
|
||||
},
|
||||
|
||||
unmounted(src, binding, vn) {
|
||||
binding.value(0, 0);
|
||||
src._get_size_ro_.unobserve(src);
|
||||
}
|
||||
} as Directive;
|
|
@ -2,6 +2,7 @@ import { App } from 'vue';
|
|||
|
||||
import userPreview from './user-preview';
|
||||
import size from './size';
|
||||
import getSize from './get-size';
|
||||
import particle from './particle';
|
||||
import tooltip from './tooltip';
|
||||
import hotkey from './hotkey';
|
||||
|
@ -14,6 +15,7 @@ export default function(app: App) {
|
|||
app.directive('userPreview', userPreview);
|
||||
app.directive('user-preview', userPreview);
|
||||
app.directive('size', size);
|
||||
app.directive('get-size', getSize);
|
||||
app.directive('particle', particle);
|
||||
app.directive('tooltip', tooltip);
|
||||
app.directive('hotkey', hotkey);
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
<template>
|
||||
<div class="_section">
|
||||
<MkPagination :pagination="pagination" #default="{items}" class="ruryvtyk _content">
|
||||
<section class="_card announcement _gap" v-for="(announcement, i) in items" :key="announcement.id">
|
||||
<div class="_title"><span v-if="$i && !announcement.isRead">🆕 </span>{{ announcement.title }}</div>
|
||||
<div class="_content">
|
||||
<Mfm :text="announcement.text"/>
|
||||
<img v-if="announcement.imageUrl" :src="announcement.imageUrl"/>
|
||||
</div>
|
||||
<div class="_footer" v-if="$i && !announcement.isRead">
|
||||
<MkButton @click="read(items, announcement, i)" primary><i class="fas fa-check"></i> {{ $ts.gotIt }}</MkButton>
|
||||
</div>
|
||||
</section>
|
||||
</MkPagination>
|
||||
<div>
|
||||
<MkHeader :info="header"/>
|
||||
<div class="_section">
|
||||
<MkPagination :pagination="pagination" #default="{items}" class="ruryvtyk _content">
|
||||
<section class="_card announcement _gap" v-for="(announcement, i) in items" :key="announcement.id">
|
||||
<div class="_title"><span v-if="$i && !announcement.isRead">🆕 </span>{{ announcement.title }}</div>
|
||||
<div class="_content">
|
||||
<Mfm :text="announcement.text"/>
|
||||
<img v-if="announcement.imageUrl" :src="announcement.imageUrl"/>
|
||||
</div>
|
||||
<div class="_footer" v-if="$i && !announcement.isRead">
|
||||
<MkButton @click="read(items, announcement, i)" primary><i class="fas fa-check"></i> {{ $ts.gotIt }}</MkButton>
|
||||
</div>
|
||||
</section>
|
||||
</MkPagination>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -35,6 +38,11 @@ export default defineComponent({
|
|||
icon: 'fas fa-broadcast-tower',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
header: {
|
||||
title: this.$ts.announcements,
|
||||
icon: 'fas fa-broadcast-tower',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'announcements',
|
||||
limit: 10,
|
||||
|
|
|
@ -89,7 +89,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
top() {
|
||||
scroll(this.$el, 0);
|
||||
scroll(this.$el, { top: 0 });
|
||||
},
|
||||
|
||||
async timetravel() {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<template>
|
||||
<div :class="$style.root">
|
||||
<XCategory v-if="tab === 'category'"/>
|
||||
<div>
|
||||
<MkHeader :info="header"/>
|
||||
<div :class="$style.root">
|
||||
<XCategory v-if="tab === 'category'"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -22,6 +25,11 @@ export default defineComponent({
|
|||
icon: 'fas fa-laugh',
|
||||
bg: 'var(--bg)',
|
||||
})),
|
||||
header: computed(() => ({
|
||||
title: this.$ts.customEmojis,
|
||||
icon: 'fas fa-laugh',
|
||||
bg: 'var(--bg)',
|
||||
})),
|
||||
tab: 'category',
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,72 +1,76 @@
|
|||
<template>
|
||||
<div class="lznhrdub _root">
|
||||
<div>
|
||||
<div class="_isolated">
|
||||
<MkInput v-model="query" :debounce="true" type="search">
|
||||
<template #prefix><i class="fas fa-search"></i></template>
|
||||
<template #label>{{ $ts.searchUser }}</template>
|
||||
</MkInput>
|
||||
</div>
|
||||
<div>
|
||||
<MkHeader :info="header"/>
|
||||
|
||||
<XUserList v-if="query" class="_gap" :pagination="searchPagination" ref="search"/>
|
||||
|
||||
<div class="localfedi7 _block _isolated" v-if="meta && stats && tag == null" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }">
|
||||
<header><span>{{ $t('explore', { host: meta.name || 'Misskey' }) }}</span></header>
|
||||
<div><span>{{ $t('exploreUsersCount', { count: num(stats.originalUsersCount) }) }}</span></div>
|
||||
</div>
|
||||
|
||||
<template v-if="tag == null">
|
||||
<MkFolder class="_gap" persist-key="explore-pinned-users">
|
||||
<template #header><i class="fas fa-bookmark fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.pinnedUsers }}</template>
|
||||
<XUserList :pagination="pinnedUsers"/>
|
||||
</MkFolder>
|
||||
<MkFolder class="_gap" persist-key="explore-popular-users">
|
||||
<template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template>
|
||||
<XUserList :pagination="popularUsers"/>
|
||||
</MkFolder>
|
||||
<MkFolder class="_gap" persist-key="explore-recently-updated-users">
|
||||
<template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template>
|
||||
<XUserList :pagination="recentlyUpdatedUsers"/>
|
||||
</MkFolder>
|
||||
<MkFolder class="_gap" persist-key="explore-recently-registered-users">
|
||||
<template #header><i class="fas fa-plus fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyRegisteredUsers }}</template>
|
||||
<XUserList :pagination="recentlyRegisteredUsers"/>
|
||||
</MkFolder>
|
||||
</template>
|
||||
</div>
|
||||
<div>
|
||||
<div class="localfedi7 _block _isolated" v-if="tag == null" :style="{ backgroundImage: `url(/static-assets/client/fedi.jpg)` }">
|
||||
<header><span>{{ $ts.exploreFediverse }}</span></header>
|
||||
</div>
|
||||
|
||||
<MkFolder :foldable="true" :expanded="false" ref="tags" class="_gap">
|
||||
<template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularTags }}</template>
|
||||
|
||||
<div class="vxjfqztj">
|
||||
<MkA v-for="tag in tagsLocal" :to="`/explore/tags/${tag.tag}`" :key="'local:' + tag.tag" class="local">{{ tag.tag }}</MkA>
|
||||
<MkA v-for="tag in tagsRemote" :to="`/explore/tags/${tag.tag}`" :key="'remote:' + tag.tag">{{ tag.tag }}</MkA>
|
||||
<div class="lznhrdub _root">
|
||||
<div>
|
||||
<div class="_isolated">
|
||||
<MkInput v-model="query" :debounce="true" type="search">
|
||||
<template #prefix><i class="fas fa-search"></i></template>
|
||||
<template #label>{{ $ts.searchUser }}</template>
|
||||
</MkInput>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder v-if="tag != null" :key="`${tag}`" class="_gap">
|
||||
<template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ tag }}</template>
|
||||
<XUserList :pagination="tagUsers"/>
|
||||
</MkFolder>
|
||||
<XUserList v-if="query" class="_gap" :pagination="searchPagination" ref="search"/>
|
||||
|
||||
<template v-if="tag == null">
|
||||
<MkFolder class="_gap">
|
||||
<template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template>
|
||||
<XUserList :pagination="popularUsersF"/>
|
||||
<div class="localfedi7 _block _isolated" v-if="meta && stats && tag == null" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }">
|
||||
<header><span>{{ $t('explore', { host: meta.name || 'Misskey' }) }}</span></header>
|
||||
<div><span>{{ $t('exploreUsersCount', { count: num(stats.originalUsersCount) }) }}</span></div>
|
||||
</div>
|
||||
|
||||
<template v-if="tag == null">
|
||||
<MkFolder class="_gap" persist-key="explore-pinned-users">
|
||||
<template #header><i class="fas fa-bookmark fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.pinnedUsers }}</template>
|
||||
<XUserList :pagination="pinnedUsers"/>
|
||||
</MkFolder>
|
||||
<MkFolder class="_gap" persist-key="explore-popular-users">
|
||||
<template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template>
|
||||
<XUserList :pagination="popularUsers"/>
|
||||
</MkFolder>
|
||||
<MkFolder class="_gap" persist-key="explore-recently-updated-users">
|
||||
<template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template>
|
||||
<XUserList :pagination="recentlyUpdatedUsers"/>
|
||||
</MkFolder>
|
||||
<MkFolder class="_gap" persist-key="explore-recently-registered-users">
|
||||
<template #header><i class="fas fa-plus fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyRegisteredUsers }}</template>
|
||||
<XUserList :pagination="recentlyRegisteredUsers"/>
|
||||
</MkFolder>
|
||||
</template>
|
||||
</div>
|
||||
<div>
|
||||
<div class="localfedi7 _block _isolated" v-if="tag == null" :style="{ backgroundImage: `url(/static-assets/client/fedi.jpg)` }">
|
||||
<header><span>{{ $ts.exploreFediverse }}</span></header>
|
||||
</div>
|
||||
|
||||
<MkFolder :foldable="true" :expanded="false" ref="tags" class="_gap">
|
||||
<template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularTags }}</template>
|
||||
|
||||
<div class="vxjfqztj">
|
||||
<MkA v-for="tag in tagsLocal" :to="`/explore/tags/${tag.tag}`" :key="'local:' + tag.tag" class="local">{{ tag.tag }}</MkA>
|
||||
<MkA v-for="tag in tagsRemote" :to="`/explore/tags/${tag.tag}`" :key="'remote:' + tag.tag">{{ tag.tag }}</MkA>
|
||||
</div>
|
||||
</MkFolder>
|
||||
<MkFolder class="_gap">
|
||||
<template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template>
|
||||
<XUserList :pagination="recentlyUpdatedUsersF"/>
|
||||
|
||||
<MkFolder v-if="tag != null" :key="`${tag}`" class="_gap">
|
||||
<template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ tag }}</template>
|
||||
<XUserList :pagination="tagUsers"/>
|
||||
</MkFolder>
|
||||
<MkFolder class="_gap">
|
||||
<template #header><i class="fas fa-rocket fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyDiscoveredUsers }}</template>
|
||||
<XUserList :pagination="recentlyRegisteredUsersF"/>
|
||||
</MkFolder>
|
||||
</template>
|
||||
|
||||
<template v-if="tag == null">
|
||||
<MkFolder class="_gap">
|
||||
<template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template>
|
||||
<XUserList :pagination="popularUsersF"/>
|
||||
</MkFolder>
|
||||
<MkFolder class="_gap">
|
||||
<template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template>
|
||||
<XUserList :pagination="recentlyUpdatedUsersF"/>
|
||||
</MkFolder>
|
||||
<MkFolder class="_gap">
|
||||
<template #header><i class="fas fa-rocket fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyDiscoveredUsers }}</template>
|
||||
<XUserList :pagination="recentlyRegisteredUsersF"/>
|
||||
</MkFolder>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -100,6 +104,10 @@ export default defineComponent({
|
|||
title: this.$ts.explore,
|
||||
icon: 'fas fa-hashtag'
|
||||
},
|
||||
header: {
|
||||
title: this.$ts.explore,
|
||||
icon: 'fas fa-hashtag'
|
||||
},
|
||||
pinnedUsers: { endpoint: 'pinned-users' },
|
||||
popularUsers: { endpoint: 'users', limit: 10, noPaging: true, params: {
|
||||
state: 'alive',
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
<template>
|
||||
<div class="jmelgwjh">
|
||||
<div class="body">
|
||||
<XNotes class="notes" :pagination="pagination" :detail="true" :prop="'note'" @before="before()" @after="after()"/>
|
||||
<div>
|
||||
<MkHeader :info="header"/>
|
||||
<div class="jmelgwjh">
|
||||
<div class="body">
|
||||
<XNotes class="notes" :pagination="pagination" :detail="true" :prop="'note'" @before="before()" @after="after()"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -25,6 +28,11 @@ export default defineComponent({
|
|||
icon: 'fas fa-star',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
header: {
|
||||
title: this.$ts.favorites,
|
||||
icon: 'fas fa-star',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'i/favorites',
|
||||
limit: 10,
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<template>
|
||||
<div class="_section">
|
||||
<XNotes class="_content" ref="notes" :pagination="pagination" @before="before" @after="after"/>
|
||||
<div>
|
||||
<MkHeader :info="header"/>
|
||||
<div class="_section">
|
||||
<XNotes class="_content" ref="notes" :pagination="pagination" @before="before" @after="after"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -19,12 +22,18 @@ export default defineComponent({
|
|||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.featured,
|
||||
icon: 'fas fa-fire-alt'
|
||||
icon: 'fas fa-fire-alt',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
header: {
|
||||
title: this.$ts.featured,
|
||||
icon: 'fas fa-fire-alt',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'notes/featured',
|
||||
limit: 10,
|
||||
offsetMode: true
|
||||
offsetMode: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
|
|
@ -1,95 +1,98 @@
|
|||
<template>
|
||||
<div class="taeiyria">
|
||||
<div class="query">
|
||||
<MkInput v-model="host" :debounce="true" class="">
|
||||
<template #prefix><i class="fas fa-search"></i></template>
|
||||
<template #label>{{ $ts.host }}</template>
|
||||
</MkInput>
|
||||
<div class="_inputSplit">
|
||||
<MkSelect v-model="state">
|
||||
<template #label>{{ $ts.state }}</template>
|
||||
<option value="all">{{ $ts.all }}</option>
|
||||
<option value="federating">{{ $ts.federating }}</option>
|
||||
<option value="subscribing">{{ $ts.subscribing }}</option>
|
||||
<option value="publishing">{{ $ts.publishing }}</option>
|
||||
<option value="suspended">{{ $ts.suspended }}</option>
|
||||
<option value="blocked">{{ $ts.blocked }}</option>
|
||||
<option value="notResponding">{{ $ts.notResponding }}</option>
|
||||
</MkSelect>
|
||||
<MkSelect v-model="sort">
|
||||
<template #label>{{ $ts.sort }}</template>
|
||||
<option value="+pubSub">{{ $ts.pubSub }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-pubSub">{{ $ts.pubSub }} ({{ $ts.ascendingOrder }})</option>
|
||||
<option value="+notes">{{ $ts.notes }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-notes">{{ $ts.notes }} ({{ $ts.ascendingOrder }})</option>
|
||||
<option value="+users">{{ $ts.users }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-users">{{ $ts.users }} ({{ $ts.ascendingOrder }})</option>
|
||||
<option value="+following">{{ $ts.following }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-following">{{ $ts.following }} ({{ $ts.ascendingOrder }})</option>
|
||||
<option value="+followers">{{ $ts.followers }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-followers">{{ $ts.followers }} ({{ $ts.ascendingOrder }})</option>
|
||||
<option value="+caughtAt">{{ $ts.registeredAt }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-caughtAt">{{ $ts.registeredAt }} ({{ $ts.ascendingOrder }})</option>
|
||||
<option value="+lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.ascendingOrder }})</option>
|
||||
<option value="+driveUsage">{{ $ts.driveUsage }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-driveUsage">{{ $ts.driveUsage }} ({{ $ts.ascendingOrder }})</option>
|
||||
<option value="+driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.ascendingOrder }})</option>
|
||||
</MkSelect>
|
||||
<div>
|
||||
<MkHeader :info="header"/>
|
||||
<div class="taeiyria">
|
||||
<div class="query">
|
||||
<MkInput v-model="host" :debounce="true" class="">
|
||||
<template #prefix><i class="fas fa-search"></i></template>
|
||||
<template #label>{{ $ts.host }}</template>
|
||||
</MkInput>
|
||||
<div class="_inputSplit">
|
||||
<MkSelect v-model="state">
|
||||
<template #label>{{ $ts.state }}</template>
|
||||
<option value="all">{{ $ts.all }}</option>
|
||||
<option value="federating">{{ $ts.federating }}</option>
|
||||
<option value="subscribing">{{ $ts.subscribing }}</option>
|
||||
<option value="publishing">{{ $ts.publishing }}</option>
|
||||
<option value="suspended">{{ $ts.suspended }}</option>
|
||||
<option value="blocked">{{ $ts.blocked }}</option>
|
||||
<option value="notResponding">{{ $ts.notResponding }}</option>
|
||||
</MkSelect>
|
||||
<MkSelect v-model="sort">
|
||||
<template #label>{{ $ts.sort }}</template>
|
||||
<option value="+pubSub">{{ $ts.pubSub }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-pubSub">{{ $ts.pubSub }} ({{ $ts.ascendingOrder }})</option>
|
||||
<option value="+notes">{{ $ts.notes }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-notes">{{ $ts.notes }} ({{ $ts.ascendingOrder }})</option>
|
||||
<option value="+users">{{ $ts.users }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-users">{{ $ts.users }} ({{ $ts.ascendingOrder }})</option>
|
||||
<option value="+following">{{ $ts.following }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-following">{{ $ts.following }} ({{ $ts.ascendingOrder }})</option>
|
||||
<option value="+followers">{{ $ts.followers }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-followers">{{ $ts.followers }} ({{ $ts.ascendingOrder }})</option>
|
||||
<option value="+caughtAt">{{ $ts.registeredAt }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-caughtAt">{{ $ts.registeredAt }} ({{ $ts.ascendingOrder }})</option>
|
||||
<option value="+lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.ascendingOrder }})</option>
|
||||
<option value="+driveUsage">{{ $ts.driveUsage }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-driveUsage">{{ $ts.driveUsage }} ({{ $ts.ascendingOrder }})</option>
|
||||
<option value="+driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.descendingOrder }})</option>
|
||||
<option value="-driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.ascendingOrder }})</option>
|
||||
</MkSelect>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MkPagination :pagination="pagination" #default="{items}" ref="instances" :key="host + state">
|
||||
<div class="dqokceoi">
|
||||
<MkA class="instance" v-for="instance in items" :key="instance.id" :to="`/instance-info/${instance.host}`">
|
||||
<div class="host"><img :src="instance.faviconUrl">{{ instance.host }}</div>
|
||||
<div class="table">
|
||||
<div class="cell">
|
||||
<div class="key">{{ $ts.registeredAt }}</div>
|
||||
<div class="value"><MkTime :time="instance.caughtAt"/></div>
|
||||
<MkPagination :pagination="pagination" #default="{items}" ref="instances" :key="host + state">
|
||||
<div class="dqokceoi">
|
||||
<MkA class="instance" v-for="instance in items" :key="instance.id" :to="`/instance-info/${instance.host}`">
|
||||
<div class="host"><img :src="instance.faviconUrl">{{ instance.host }}</div>
|
||||
<div class="table">
|
||||
<div class="cell">
|
||||
<div class="key">{{ $ts.registeredAt }}</div>
|
||||
<div class="value"><MkTime :time="instance.caughtAt"/></div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="key">{{ $ts.software }}</div>
|
||||
<div class="value">{{ instance.softwareName || `(${$ts.unknown})` }}</div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="key">{{ $ts.version }}</div>
|
||||
<div class="value">{{ instance.softwareVersion || `(${$ts.unknown})` }}</div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="key">{{ $ts.users }}</div>
|
||||
<div class="value">{{ instance.usersCount }}</div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="key">{{ $ts.notes }}</div>
|
||||
<div class="value">{{ instance.notesCount }}</div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="key">{{ $ts.sent }}</div>
|
||||
<div class="value"><MkTime v-if="instance.latestRequestSentAt" :time="instance.latestRequestSentAt"/><span v-else>N/A</span></div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="key">{{ $ts.received }}</div>
|
||||
<div class="value"><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="key">{{ $ts.software }}</div>
|
||||
<div class="value">{{ instance.softwareName || `(${$ts.unknown})` }}</div>
|
||||
<div class="footer">
|
||||
<span class="status" :class="getStatus(instance)">{{ getStatus(instance) }}</span>
|
||||
<span class="pubSub">
|
||||
<span class="sub" v-if="instance.followersCount > 0"><i class="fas fa-caret-down icon"></i>Sub</span>
|
||||
<span class="sub" v-else><i class="fas fa-caret-down icon"></i>-</span>
|
||||
<span class="pub" v-if="instance.followingCount > 0"><i class="fas fa-caret-up icon"></i>Pub</span>
|
||||
<span class="pub" v-else><i class="fas fa-caret-up icon"></i>-</span>
|
||||
</span>
|
||||
<span class="right">
|
||||
<span class="latestStatus">{{ instance.latestStatus || '-' }}</span>
|
||||
<span class="lastCommunicatedAt"><MkTime :time="instance.lastCommunicatedAt"/></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="key">{{ $ts.version }}</div>
|
||||
<div class="value">{{ instance.softwareVersion || `(${$ts.unknown})` }}</div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="key">{{ $ts.users }}</div>
|
||||
<div class="value">{{ instance.usersCount }}</div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="key">{{ $ts.notes }}</div>
|
||||
<div class="value">{{ instance.notesCount }}</div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="key">{{ $ts.sent }}</div>
|
||||
<div class="value"><MkTime v-if="instance.latestRequestSentAt" :time="instance.latestRequestSentAt"/><span v-else>N/A</span></div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="key">{{ $ts.received }}</div>
|
||||
<div class="value"><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<span class="status" :class="getStatus(instance)">{{ getStatus(instance) }}</span>
|
||||
<span class="pubSub">
|
||||
<span class="sub" v-if="instance.followersCount > 0"><i class="fas fa-caret-down icon"></i>Sub</span>
|
||||
<span class="sub" v-else><i class="fas fa-caret-down icon"></i>-</span>
|
||||
<span class="pub" v-if="instance.followingCount > 0"><i class="fas fa-caret-up icon"></i>Pub</span>
|
||||
<span class="pub" v-else><i class="fas fa-caret-up icon"></i>-</span>
|
||||
</span>
|
||||
<span class="right">
|
||||
<span class="latestStatus">{{ instance.latestStatus || '-' }}</span>
|
||||
<span class="lastCommunicatedAt"><MkTime :time="instance.lastCommunicatedAt"/></span>
|
||||
</span>
|
||||
</div>
|
||||
</MkA>
|
||||
</div>
|
||||
</MkPagination>
|
||||
</MkA>
|
||||
</div>
|
||||
</MkPagination>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -119,6 +122,11 @@ export default defineComponent({
|
|||
icon: 'fas fa-globe',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
header: {
|
||||
title: this.$ts.federation,
|
||||
icon: 'fas fa-globe',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
host: '',
|
||||
state: 'federating',
|
||||
sort: '+pubSub',
|
||||
|
|
|
@ -1,43 +1,45 @@
|
|||
<template>
|
||||
<div class="uqshojas">
|
||||
<MkButton @click="add()" primary style="margin: 0 auto 16px auto;"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton>
|
||||
<section class="_card _gap ads" v-for="ad in ads">
|
||||
<div class="_content ad">
|
||||
<MkAd v-if="ad.url" :specify="ad"/>
|
||||
<MkInput v-model="ad.url" type="url">
|
||||
<template #label>URL</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="ad.imageUrl">
|
||||
<template #label>{{ $ts.imageUrl }}</template>
|
||||
</MkInput>
|
||||
<div style="margin: 32px 0;">
|
||||
<MkRadio v-model="ad.place" value="square">square</MkRadio>
|
||||
<MkRadio v-model="ad.place" value="horizontal">horizontal</MkRadio>
|
||||
<MkRadio v-model="ad.place" value="horizontal-big">horizontal-big</MkRadio>
|
||||
<div>
|
||||
<MkHeader :info="header"/>
|
||||
<div class="uqshojas">
|
||||
<section class="_card _gap ads" v-for="ad in ads">
|
||||
<div class="_content ad">
|
||||
<MkAd v-if="ad.url" :specify="ad"/>
|
||||
<MkInput v-model="ad.url" type="url">
|
||||
<template #label>URL</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="ad.imageUrl">
|
||||
<template #label>{{ $ts.imageUrl }}</template>
|
||||
</MkInput>
|
||||
<div style="margin: 32px 0;">
|
||||
<MkRadio v-model="ad.place" value="square">square</MkRadio>
|
||||
<MkRadio v-model="ad.place" value="horizontal">horizontal</MkRadio>
|
||||
<MkRadio v-model="ad.place" value="horizontal-big">horizontal-big</MkRadio>
|
||||
</div>
|
||||
<!--
|
||||
<div style="margin: 32px 0;">
|
||||
{{ $ts.priority }}
|
||||
<MkRadio v-model="ad.priority" value="high">{{ $ts.high }}</MkRadio>
|
||||
<MkRadio v-model="ad.priority" value="middle">{{ $ts.middle }}</MkRadio>
|
||||
<MkRadio v-model="ad.priority" value="low">{{ $ts.low }}</MkRadio>
|
||||
</div>
|
||||
-->
|
||||
<MkInput v-model="ad.ratio" type="number">
|
||||
<template #label>{{ $ts.ratio }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="ad.expiresAt" type="date">
|
||||
<template #label>{{ $ts.expiration }}</template>
|
||||
</MkInput>
|
||||
<MkTextarea v-model="ad.memo">
|
||||
<template #label>{{ $ts.memo }}</template>
|
||||
</MkTextarea>
|
||||
<div class="buttons">
|
||||
<MkButton class="button" inline @click="save(ad)" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
|
||||
<MkButton class="button" inline @click="remove(ad)" danger><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
<!--
|
||||
<div style="margin: 32px 0;">
|
||||
{{ $ts.priority }}
|
||||
<MkRadio v-model="ad.priority" value="high">{{ $ts.high }}</MkRadio>
|
||||
<MkRadio v-model="ad.priority" value="middle">{{ $ts.middle }}</MkRadio>
|
||||
<MkRadio v-model="ad.priority" value="low">{{ $ts.low }}</MkRadio>
|
||||
</div>
|
||||
-->
|
||||
<MkInput v-model="ad.ratio" type="number">
|
||||
<template #label>{{ $ts.ratio }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="ad.expiresAt" type="date">
|
||||
<template #label>{{ $ts.expiration }}</template>
|
||||
</MkInput>
|
||||
<MkTextarea v-model="ad.memo">
|
||||
<template #label>{{ $ts.memo }}</template>
|
||||
</MkTextarea>
|
||||
<div class="buttons">
|
||||
<MkButton class="button" inline @click="save(ad)" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
|
||||
<MkButton class="button" inline @click="remove(ad)" danger><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -67,6 +69,17 @@ export default defineComponent({
|
|||
icon: 'fas fa-audio-description',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
header: {
|
||||
title: this.$ts.ads,
|
||||
icon: 'fas fa-audio-description',
|
||||
bg: 'var(--bg)',
|
||||
actions: [{
|
||||
asFullButton: true,
|
||||
icon: 'fas fa-plus',
|
||||
text: this.$ts.add,
|
||||
handler: this.add,
|
||||
}],
|
||||
},
|
||||
ads: [],
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,24 +1,27 @@
|
|||
<template>
|
||||
<div class="ztgjmzrw">
|
||||
<MkButton @click="add()" primary style="margin: 0 auto 16px auto;"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton>
|
||||
<section class="_card _gap announcements" v-for="announcement in announcements">
|
||||
<div class="_content announcement">
|
||||
<MkInput v-model="announcement.title">
|
||||
<template #label>{{ $ts.title }}</template>
|
||||
</MkInput>
|
||||
<MkTextarea v-model="announcement.text">
|
||||
<template #label>{{ $ts.text }}</template>
|
||||
</MkTextarea>
|
||||
<MkInput v-model="announcement.imageUrl">
|
||||
<template #label>{{ $ts.imageUrl }}</template>
|
||||
</MkInput>
|
||||
<p v-if="announcement.reads">{{ $t('nUsersRead', { n: announcement.reads }) }}</p>
|
||||
<div class="buttons">
|
||||
<MkButton class="button" inline @click="save(announcement)" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
|
||||
<MkButton class="button" inline @click="remove(announcement)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
|
||||
<div>
|
||||
<MkHeader :info="header"/>
|
||||
|
||||
<div class="ztgjmzrw">
|
||||
<section class="_card _gap announcements" v-for="announcement in announcements">
|
||||
<div class="_content announcement">
|
||||
<MkInput v-model="announcement.title">
|
||||
<template #label>{{ $ts.title }}</template>
|
||||
</MkInput>
|
||||
<MkTextarea v-model="announcement.text">
|
||||
<template #label>{{ $ts.text }}</template>
|
||||
</MkTextarea>
|
||||
<MkInput v-model="announcement.imageUrl">
|
||||
<template #label>{{ $ts.imageUrl }}</template>
|
||||
</MkInput>
|
||||
<p v-if="announcement.reads">{{ $t('nUsersRead', { n: announcement.reads }) }}</p>
|
||||
<div class="buttons">
|
||||
<MkButton class="button" inline @click="save(announcement)" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
|
||||
<MkButton class="button" inline @click="remove(announcement)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -46,6 +49,17 @@ export default defineComponent({
|
|||
icon: 'fas fa-broadcast-tower',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
header: {
|
||||
title: this.$ts.announcements,
|
||||
icon: 'fas fa-broadcast-tower',
|
||||
bg: 'var(--bg)',
|
||||
actions: [{
|
||||
asFullButton: true,
|
||||
icon: 'fas fa-plus',
|
||||
text: this.$ts.add,
|
||||
handler: this.add,
|
||||
}],
|
||||
},
|
||||
announcements: [],
|
||||
}
|
||||
},
|
||||
|
|
|
@ -11,13 +11,13 @@
|
|||
<div class="_monolithic_">
|
||||
<div class="yigymqpb _section">
|
||||
<img :src="emoji.url" class="img"/>
|
||||
<MkInput v-model="name">
|
||||
<MkInput class="_formBlock" v-model="name">
|
||||
<template #label>{{ $ts.name }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="category" :datalist="categories">
|
||||
<MkInput class="_formBlock" v-model="category" :datalist="categories">
|
||||
<template #label>{{ $ts.category }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="aliases">
|
||||
<MkInput class="_formBlock" v-model="aliases">
|
||||
<template #label>{{ $ts.tags }}</template>
|
||||
<template #caption>{{ $ts.setMultipleBySeparatingWithSpace }}</template>
|
||||
</MkInput>
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
<template>
|
||||
<div class="ogwlenmc">
|
||||
<MkTab v-model="tab">
|
||||
<option value="local">{{ $ts.local }}</option>
|
||||
<option value="remote">{{ $ts.remote }}</option>
|
||||
</MkTab>
|
||||
<MkHeader :info="header"/>
|
||||
|
||||
<div class="local" v-if="tab === 'local'">
|
||||
<MkButton primary @click="add" style="margin: var(--margin) auto;"><i class="fas fa-plus"></i> {{ $ts.addEmoji }}</MkButton>
|
||||
<MkInput v-model="query" :debounce="true" type="search" style="margin: var(--margin);">
|
||||
<template #prefix><i class="fas fa-search"></i></template>
|
||||
<template #label>{{ $ts.search }}</template>
|
||||
|
@ -79,11 +75,27 @@ export default defineComponent({
|
|||
title: this.$ts.customEmojis,
|
||||
icon: 'fas fa-laugh',
|
||||
bg: 'var(--bg)',
|
||||
action: {
|
||||
icon: 'fas fa-plus',
|
||||
handler: this.add
|
||||
}
|
||||
},
|
||||
header: computed(() => ({
|
||||
title: this.$ts.customEmojis,
|
||||
icon: 'fas fa-laugh',
|
||||
bg: 'var(--bg)',
|
||||
actions: [{
|
||||
asFullButton: true,
|
||||
icon: 'fas fa-plus',
|
||||
text: this.$ts.addEmoji,
|
||||
handler: this.add,
|
||||
}],
|
||||
tabs: [{
|
||||
active: this.tab === 'local',
|
||||
title: this.$ts.local,
|
||||
onClick: () => { this.tab = 'local'; },
|
||||
}, {
|
||||
active: this.tab === 'remote',
|
||||
title: this.$ts.remote,
|
||||
onClick: () => { this.tab = 'remote'; },
|
||||
},]
|
||||
})),
|
||||
tab: 'local',
|
||||
query: null,
|
||||
queryRemote: null,
|
||||
|
|
|
@ -79,7 +79,8 @@ export default defineComponent({
|
|||
setup(props, context) {
|
||||
const indexInfo = {
|
||||
title: i18n.locale.instance,
|
||||
icon: 'fas fa-cog'
|
||||
icon: 'fas fa-cog',
|
||||
bg: 'var(--bg)',
|
||||
};
|
||||
const INFO = ref(indexInfo);
|
||||
const page = ref(props.initialPage);
|
||||
|
@ -126,7 +127,7 @@ export default defineComponent({
|
|||
pageProps.value = {};
|
||||
|
||||
nextTick(() => {
|
||||
scroll(el.value, 0);
|
||||
scroll(el.value, { top: 0 });
|
||||
});
|
||||
}, { immediate: true });
|
||||
|
||||
|
@ -234,7 +235,6 @@ export default defineComponent({
|
|||
> .main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: auto;
|
||||
--baseContentWidth: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
<FormSwitch v-model="enableRegistration">{{ $ts.enableRegistration }}</FormSwitch>
|
||||
|
||||
<FormSwitch v-model="emailRequiredForSignup">{{ $ts.emailRequiredForSignup }}</FormSwitch>
|
||||
|
||||
<FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
|
||||
</FormSuspense>
|
||||
</FormBase>
|
||||
|
@ -50,6 +52,7 @@ export default defineComponent({
|
|||
enableHcaptcha: false,
|
||||
enableRecaptcha: false,
|
||||
enableRegistration: false,
|
||||
emailRequiredForSignup: false,
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -63,11 +66,13 @@ export default defineComponent({
|
|||
this.enableHcaptcha = meta.enableHcaptcha;
|
||||
this.enableRecaptcha = meta.enableRecaptcha;
|
||||
this.enableRegistration = !meta.disableRegistration;
|
||||
this.emailRequiredForSignup = meta.emailRequiredForSignup;
|
||||
},
|
||||
|
||||
save() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
disableRegistration: !this.enableRegistration,
|
||||
emailRequiredForSignup: this.emailRequiredForSignup,
|
||||
}).then(() => {
|
||||
fetchInstance();
|
||||
});
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
<template>
|
||||
<div class="lknzcolw">
|
||||
<div class="actions">
|
||||
<MkButton inline primary @click="addUser()"><i class="fas fa-plus"></i> {{ $ts.addUser }}</MkButton>
|
||||
<MkButton inline primary @click="lookupUser()"><i class="fas fa-search"></i> {{ $ts.lookup }}</MkButton>
|
||||
</div>
|
||||
<MkHeader :info="header"/>
|
||||
|
||||
<div class="users">
|
||||
<div class="inputs">
|
||||
|
@ -91,10 +88,26 @@ export default defineComponent({
|
|||
title: this.$ts.users,
|
||||
icon: 'fas fa-users',
|
||||
bg: 'var(--bg)',
|
||||
action: {
|
||||
},
|
||||
header: {
|
||||
title: this.$ts.users,
|
||||
icon: 'fas fa-users',
|
||||
bg: 'var(--bg)',
|
||||
actions: [{
|
||||
icon: 'fas fa-search',
|
||||
text: this.$ts.search,
|
||||
handler: this.searchUser
|
||||
}
|
||||
}, {
|
||||
asFullButton: true,
|
||||
icon: 'fas fa-plus',
|
||||
text: this.$ts.addUser,
|
||||
handler: this.addUser
|
||||
}, {
|
||||
asFullButton: true,
|
||||
icon: 'fas fa-search',
|
||||
text: this.$ts.lookup,
|
||||
handler: this.lookupUser
|
||||
}]
|
||||
},
|
||||
sort: '+createdAt',
|
||||
state: 'all',
|
||||
|
@ -173,10 +186,6 @@ export default defineComponent({
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.lknzcolw {
|
||||
> .actions {
|
||||
margin: var(--margin);
|
||||
}
|
||||
|
||||
> .users {
|
||||
margin: var(--margin);
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<template>
|
||||
<div class="_section">
|
||||
<XNotes class="_content" :pagination="pagination" @before="before()" @after="after()"/>
|
||||
<div>
|
||||
<MkHeader :info="header"/>
|
||||
<div class="_section">
|
||||
<XNotes class="_content" :pagination="pagination" @before="before()" @after="after()"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -19,7 +22,13 @@ export default defineComponent({
|
|||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.mentions,
|
||||
icon: 'fas fa-at'
|
||||
icon: 'fas fa-at',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
header: {
|
||||
title: this.$ts.mentions,
|
||||
icon: 'fas fa-at',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'notes/mentions',
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<template>
|
||||
<div>
|
||||
<XNotes :pagination="pagination" @before="before()" @after="after()"/>
|
||||
<MkHeader :info="header"/>
|
||||
<div>
|
||||
<XNotes :pagination="pagination" @before="before()" @after="after()"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -19,7 +22,13 @@ export default defineComponent({
|
|||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.directNotes,
|
||||
icon: 'fas fa-envelope'
|
||||
icon: 'fas fa-envelope',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
header: {
|
||||
title: this.$ts.directNotes,
|
||||
icon: 'fas fa-envelope',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'notes/mentions',
|
||||
|
|
|
@ -1,38 +1,42 @@
|
|||
<template>
|
||||
<div class="yweeujhr _root" v-size="{ max: [400] }">
|
||||
<MkButton @click="start" primary class="start"><i class="fas fa-plus"></i> {{ $ts.startMessaging }}</MkButton>
|
||||
<div>
|
||||
<MkHeader :info="header"/>
|
||||
|
||||
<div class="history" v-if="messages.length > 0">
|
||||
<MkA v-for="(message, i) in messages"
|
||||
class="message _block"
|
||||
:class="{ isMe: isMe(message), isRead: message.groupId ? message.reads.includes($i.id) : message.isRead }"
|
||||
:to="message.groupId ? `/my/messaging/group/${message.groupId}` : `/my/messaging/${getAcct(isMe(message) ? message.recipient : message.user)}`"
|
||||
:data-index="i"
|
||||
:key="message.id"
|
||||
v-anim="i"
|
||||
>
|
||||
<div>
|
||||
<MkAvatar class="avatar" :user="message.groupId ? message.user : isMe(message) ? message.recipient : message.user" :show-indicator="true"/>
|
||||
<header v-if="message.groupId">
|
||||
<span class="name">{{ message.group.name }}</span>
|
||||
<MkTime :time="message.createdAt" class="time"/>
|
||||
</header>
|
||||
<header v-else>
|
||||
<span class="name"><MkUserName :user="isMe(message) ? message.recipient : message.user"/></span>
|
||||
<span class="username">@{{ acct(isMe(message) ? message.recipient : message.user) }}</span>
|
||||
<MkTime :time="message.createdAt" class="time"/>
|
||||
</header>
|
||||
<div class="body">
|
||||
<p class="text"><span class="me" v-if="isMe(message)">{{ $ts.you }}:</span>{{ message.text }}</p>
|
||||
<div class="yweeujhr _root" v-size="{ max: [400] }">
|
||||
<MkButton @click="start" primary class="start"><i class="fas fa-plus"></i> {{ $ts.startMessaging }}</MkButton>
|
||||
|
||||
<div class="history" v-if="messages.length > 0">
|
||||
<MkA v-for="(message, i) in messages"
|
||||
class="message _block"
|
||||
:class="{ isMe: isMe(message), isRead: message.groupId ? message.reads.includes($i.id) : message.isRead }"
|
||||
:to="message.groupId ? `/my/messaging/group/${message.groupId}` : `/my/messaging/${getAcct(isMe(message) ? message.recipient : message.user)}`"
|
||||
:data-index="i"
|
||||
:key="message.id"
|
||||
v-anim="i"
|
||||
>
|
||||
<div>
|
||||
<MkAvatar class="avatar" :user="message.groupId ? message.user : isMe(message) ? message.recipient : message.user" :show-indicator="true"/>
|
||||
<header v-if="message.groupId">
|
||||
<span class="name">{{ message.group.name }}</span>
|
||||
<MkTime :time="message.createdAt" class="time"/>
|
||||
</header>
|
||||
<header v-else>
|
||||
<span class="name"><MkUserName :user="isMe(message) ? message.recipient : message.user"/></span>
|
||||
<span class="username">@{{ acct(isMe(message) ? message.recipient : message.user) }}</span>
|
||||
<MkTime :time="message.createdAt" class="time"/>
|
||||
</header>
|
||||
<div class="body">
|
||||
<p class="text"><span class="me" v-if="isMe(message)">{{ $ts.you }}:</span>{{ message.text }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MkA>
|
||||
</MkA>
|
||||
</div>
|
||||
<div class="_fullinfo" v-if="!fetching && messages.length == 0">
|
||||
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
||||
<div>{{ $ts.noHistory }}</div>
|
||||
</div>
|
||||
<MkLoading v-if="fetching"/>
|
||||
</div>
|
||||
<div class="_fullinfo" v-if="!fetching && messages.length == 0">
|
||||
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
||||
<div>{{ $ts.noHistory }}</div>
|
||||
</div>
|
||||
<MkLoading v-if="fetching"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -56,6 +60,11 @@ export default defineComponent({
|
|||
icon: 'fas fa-comments',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
header: {
|
||||
title: this.$ts.messaging,
|
||||
icon: 'fas fa-comments',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
fetching: true,
|
||||
moreFetching: false,
|
||||
messages: [],
|
||||
|
|
|
@ -284,7 +284,7 @@ const Component = defineComponent({
|
|||
},
|
||||
|
||||
scrollToBottom() {
|
||||
scroll(this.$el, this.$el.offsetHeight);
|
||||
scroll(this.$el, { top: this.$el.offsetHeight });
|
||||
},
|
||||
|
||||
onIndicatorClick() {
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
<template>
|
||||
<div class="qkcjvfiv">
|
||||
<MkButton @click="create" primary class="add"><i class="fas fa-plus"></i> {{ $ts.createList }}</MkButton>
|
||||
<div>
|
||||
<MkHeader :info="header"/>
|
||||
<div class="qkcjvfiv">
|
||||
<MkButton @click="create" primary class="add"><i class="fas fa-plus"></i> {{ $ts.createList }}</MkButton>
|
||||
|
||||
<MkPagination :pagination="pagination" #default="{items}" class="lists _content" ref="list">
|
||||
<MkA v-for="list in items" :key="list.id" class="list _panel" :to="`/my/lists/${ list.id }`">
|
||||
<div class="name">{{ list.name }}</div>
|
||||
<MkAvatars :user-ids="list.userIds"/>
|
||||
</MkA>
|
||||
</MkPagination>
|
||||
<MkPagination :pagination="pagination" #default="{items}" class="lists _content" ref="list">
|
||||
<MkA v-for="list in items" :key="list.id" class="list _panel" :to="`/my/lists/${ list.id }`">
|
||||
<div class="name">{{ list.name }}</div>
|
||||
<MkAvatars :user-ids="list.userIds"/>
|
||||
</MkA>
|
||||
</MkPagination>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -31,6 +34,12 @@ export default defineComponent({
|
|||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.manageLists,
|
||||
icon: 'fas fa-list-ul',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
header: {
|
||||
title: this.$ts.manageLists,
|
||||
icon: 'fas fa-list-ul',
|
||||
bg: 'var(--bg)',
|
||||
action: {
|
||||
icon: 'fas fa-plus',
|
||||
handler: this.create
|
||||
|
|
|
@ -1,34 +1,37 @@
|
|||
<template>
|
||||
<div class="mk-list-page">
|
||||
<transition name="zoom" mode="out-in">
|
||||
<div v-if="list" class="_section">
|
||||
<div class="_content">
|
||||
<MkButton inline @click="addUser()">{{ $ts.addUser }}</MkButton>
|
||||
<MkButton inline @click="renameList()">{{ $ts.rename }}</MkButton>
|
||||
<MkButton inline @click="deleteList()">{{ $ts.delete }}</MkButton>
|
||||
<div>
|
||||
<MkHeader v-if="header" :info="header"/>
|
||||
<div class="mk-list-page">
|
||||
<transition name="zoom" mode="out-in">
|
||||
<div v-if="list" class="_section">
|
||||
<div class="_content">
|
||||
<MkButton inline @click="addUser()">{{ $ts.addUser }}</MkButton>
|
||||
<MkButton inline @click="renameList()">{{ $ts.rename }}</MkButton>
|
||||
<MkButton inline @click="deleteList()">{{ $ts.delete }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</transition>
|
||||
|
||||
<transition name="zoom" mode="out-in">
|
||||
<div v-if="list" class="_section members _gap">
|
||||
<div class="_title">{{ $ts.members }}</div>
|
||||
<div class="_content">
|
||||
<div class="users">
|
||||
<div class="user _panel" v-for="user in users" :key="user.id">
|
||||
<MkAvatar :user="user" class="avatar" :show-indicator="true"/>
|
||||
<div class="body">
|
||||
<MkUserName :user="user" class="name"/>
|
||||
<MkAcct :user="user" class="acct"/>
|
||||
</div>
|
||||
<div class="action">
|
||||
<button class="_button" @click="removeUser(user)"><i class="fas fa-times"></i></button>
|
||||
<transition name="zoom" mode="out-in">
|
||||
<div v-if="list" class="_section members _gap">
|
||||
<div class="_title">{{ $ts.members }}</div>
|
||||
<div class="_content">
|
||||
<div class="users">
|
||||
<div class="user _panel" v-for="user in users" :key="user.id">
|
||||
<MkAvatar :user="user" class="avatar" :show-indicator="true"/>
|
||||
<div class="body">
|
||||
<MkUserName :user="user" class="name"/>
|
||||
<MkAcct :user="user" class="acct"/>
|
||||
</div>
|
||||
<div class="action">
|
||||
<button class="_button" @click="removeUser(user)"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -50,6 +53,10 @@ export default defineComponent({
|
|||
title: this.list.name,
|
||||
icon: 'fas fa-list-ul',
|
||||
} : null),
|
||||
header: computed(() => this.list ? {
|
||||
title: this.list.name,
|
||||
icon: 'fas fa-list-ul',
|
||||
} : null),
|
||||
list: null,
|
||||
users: [],
|
||||
};
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
<template>
|
||||
<div class="clupoqwt" v-size="{ min: [800] }">
|
||||
<XNotifications class="notifications" @before="before" @after="after" page/>
|
||||
<div>
|
||||
<MkHeader :info="header"/>
|
||||
<div class="clupoqwt" v-size="{ min: [800] }">
|
||||
<XNotifications class="notifications" @before="before" @after="after" :include-types="includeTypes" :unread-only="tab === 'unread'"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import Progress from '@client/scripts/loading';
|
||||
import XNotifications from '@client/components/notifications.vue';
|
||||
import * as os from '@client/os';
|
||||
import * as symbols from '@client/symbols';
|
||||
import { notificationTypes } from '@/types';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -22,14 +26,35 @@ export default defineComponent({
|
|||
title: this.$ts.notifications,
|
||||
icon: 'fas fa-bell',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
tab: 'all',
|
||||
includeTypes: null,
|
||||
header: computed(() => ({
|
||||
title: this.$ts.notifications,
|
||||
icon: 'fas fa-bell',
|
||||
bg: 'var(--bg)',
|
||||
actions: [{
|
||||
text: this.$ts.filter,
|
||||
icon: 'fas fa-filter',
|
||||
highlighted: this.includeTypes != null,
|
||||
handler: this.setFilter,
|
||||
}, {
|
||||
text: this.$ts.markAllAsRead,
|
||||
icon: 'fas fa-check',
|
||||
handler: () => {
|
||||
os.apiWithDialog('notifications/mark-all-as-read');
|
||||
}
|
||||
}]
|
||||
},
|
||||
},
|
||||
}],
|
||||
tabs: [{
|
||||
active: this.tab === 'all',
|
||||
title: this.$ts.all,
|
||||
onClick: () => { this.tab = 'all'; },
|
||||
}, {
|
||||
active: this.tab === 'unread',
|
||||
title: this.$ts.unread,
|
||||
onClick: () => { this.tab = 'unread'; },
|
||||
},]
|
||||
})),
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -40,6 +65,24 @@ export default defineComponent({
|
|||
|
||||
after() {
|
||||
Progress.done();
|
||||
},
|
||||
|
||||
setFilter(ev) {
|
||||
const typeItems = notificationTypes.map(t => ({
|
||||
text: this.$t(`_notification._types.${t}`),
|
||||
active: this.includeTypes && this.includeTypes.includes(t),
|
||||
action: () => {
|
||||
this.includeTypes = [t];
|
||||
}
|
||||
}));
|
||||
const items = this.includeTypes != null ? [{
|
||||
icon: 'fas fa-times',
|
||||
text: this.$ts.clear,
|
||||
action: () => {
|
||||
this.includeTypes = null;
|
||||
}
|
||||
}, null, ...typeItems] : typeItems;
|
||||
os.popupMenu(items, ev.currentTarget || ev.target);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<template>
|
||||
<div>
|
||||
<MkHeader :info="header"/>
|
||||
|
||||
<!-- TODO: MkHeaderに統合 -->
|
||||
<MkTab v-model="tab" v-if="$i">
|
||||
<option value="featured"><i class="fas fa-fire-alt"></i> {{ $ts._pages.featured }}</option>
|
||||
<option value="my"><i class="fas fa-edit"></i> {{ $ts._pages.my }}</option>
|
||||
|
@ -46,11 +49,17 @@ export default defineComponent({
|
|||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.pages,
|
||||
icon: 'fas fa-sticky-note',
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
header: {
|
||||
title: this.$ts.pages,
|
||||
icon: 'fas fa-sticky-note',
|
||||
bg: 'var(--bg)',
|
||||
actions: [{
|
||||
icon: 'fas fa-plus',
|
||||
text: this.$ts.create,
|
||||
handler: this.create
|
||||
}]
|
||||
handler: this.create,
|
||||
}],
|
||||
},
|
||||
tab: 'featured',
|
||||
featuredPagesPagination: {
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
<template>
|
||||
<div class="_section">
|
||||
<div class="_content">
|
||||
<XNotes ref="notes" :pagination="pagination" @before="before" @after="after"/>
|
||||
<div>
|
||||
<MkHeader :info="header"/>
|
||||
<div class="_section">
|
||||
<div class="_content">
|
||||
<XNotes ref="notes" :pagination="pagination" @before="before" @after="after"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -21,7 +24,11 @@ export default defineComponent({
|
|||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: computed(() => this.$t('searchWith', { q: this.$route.query.q })),
|
||||
icon: 'fas fa-search'
|
||||
icon: 'fas fa-search',
|
||||
},
|
||||
header: {
|
||||
title: computed(() => this.$t('searchWith', { q: this.$route.query.q })),
|
||||
icon: 'fas fa-search',
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'notes/search',
|
||||
|
|
|
@ -31,10 +31,10 @@
|
|||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import FormSwitch from '@client/components/form/switch.vue';
|
||||
import FormSwitch from '@client/components/debobigego/switch.vue';
|
||||
import FormLink from '@client/components/debobigego/link.vue';
|
||||
import FormRadios from '@client/components/form/radios.vue';
|
||||
import FormInput from '@client/components/form/input.vue';
|
||||
import FormRadios from '@client/components/debobigego/radios.vue';
|
||||
import FormInput from '@client/components/debobigego/input.vue';
|
||||
import FormBase from '@client/components/debobigego/base.vue';
|
||||
import FormGroup from '@client/components/debobigego/group.vue';
|
||||
import { deckStore } from '@client/ui/deck/deck-store';
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<component :is="component" :key="page" @info="onInfo" v-bind="pageProps"/>
|
||||
<component :is="component" :key="page" v-bind="pageProps"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -80,9 +80,7 @@ export default defineComponent({
|
|||
const narrow = ref(false);
|
||||
const view = ref(null);
|
||||
const el = ref(null);
|
||||
const onInfo = (viewInfo) => {
|
||||
INFO.value = viewInfo;
|
||||
};
|
||||
|
||||
const pageProps = ref({});
|
||||
const component = computed(() => {
|
||||
if (page.value == null) return null;
|
||||
|
@ -145,7 +143,7 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
nextTick(() => {
|
||||
scroll(el.value, 0);
|
||||
scroll(el.value, { top: 0 });
|
||||
});
|
||||
}, { immediate: true });
|
||||
|
||||
|
@ -175,7 +173,6 @@ export default defineComponent({
|
|||
narrow,
|
||||
view,
|
||||
el,
|
||||
onInfo,
|
||||
pageProps,
|
||||
component,
|
||||
emailNotConfigured,
|
||||
|
|
|
@ -15,7 +15,7 @@ import FormButton from '@client/components/debobigego/button.vue';
|
|||
import FormLink from '@client/components/debobigego/link.vue';
|
||||
import FormBase from '@client/components/debobigego/base.vue';
|
||||
import FormGroup from '@client/components/debobigego/group.vue';
|
||||
import { notificationTypes } from '../../../types';
|
||||
import { notificationTypes } from '@/types';
|
||||
import * as os from '@client/os';
|
||||
import * as symbols from '@client/symbols';
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ export default defineComponent({
|
|||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.profile,
|
||||
icon: 'fas fa-user',
|
||||
bg: 'var(--bg)'
|
||||
bg: 'var(--bg)',
|
||||
},
|
||||
host,
|
||||
langs,
|
||||
|
|
50
src/client/pages/signup-complete.vue
Normal file
50
src/client/pages/signup-complete.vue
Normal file
|
@ -0,0 +1,50 @@
|
|||
<template>
|
||||
<div>
|
||||
{{ $ts.processing }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import * as os from '@client/os';
|
||||
import * as symbols from '@client/symbols';
|
||||
import { login } from '@client/account';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
||||
},
|
||||
|
||||
props: {
|
||||
code: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: {
|
||||
title: this.$ts.signup,
|
||||
icon: 'fas fa-user'
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
os.apiWithDialog('signup-pending', {
|
||||
code: this.code,
|
||||
}).then(res => {
|
||||
login(res.i, '/');
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
|
@ -1,18 +1,21 @@
|
|||
<template>
|
||||
<div class="cmuxhskf" v-hotkey.global="keymap" v-size="{ min: [800] }">
|
||||
<XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _block"/>
|
||||
<XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _block" fixed/>
|
||||
<div v-hotkey.global="keymap">
|
||||
<MkHeader :info="header"/>
|
||||
<div class="cmuxhskf" v-size="{ min: [800] }">
|
||||
<XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _block"/>
|
||||
<XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _block" fixed/>
|
||||
|
||||
<div class="new" v-if="queue > 0"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div>
|
||||
<div class="tl _block">
|
||||
<XTimeline ref="tl" class="tl"
|
||||
:key="src"
|
||||
:src="src"
|
||||
:sound="true"
|
||||
@before="before()"
|
||||
@after="after()"
|
||||
@queue="queueUpdated"
|
||||
/>
|
||||
<div class="new" v-if="queue > 0"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div>
|
||||
<div class="tl _block">
|
||||
<XTimeline ref="tl" class="tl"
|
||||
:key="src"
|
||||
:src="src"
|
||||
:sound="true"
|
||||
@before="before()"
|
||||
@after="after()"
|
||||
@queue="queueUpdated"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -43,6 +46,11 @@ export default defineComponent({
|
|||
title: this.$ts.timeline,
|
||||
icon: this.src === 'local' ? 'fas fa-comments' : this.src === 'social' ? 'fas fa-share-alt' : this.src === 'global' ? 'fas fa-globe' : 'fas fa-home',
|
||||
bg: 'var(--bg)',
|
||||
})),
|
||||
header: computed(() => ({
|
||||
title: this.$ts.timeline,
|
||||
icon: this.src === 'local' ? 'fas fa-comments' : this.src === 'social' ? 'fas fa-share-alt' : this.src === 'global' ? 'fas fa-globe' : 'fas fa-home',
|
||||
bg: 'var(--bg)',
|
||||
actions: [{
|
||||
icon: 'fas fa-list-ul',
|
||||
text: this.$ts.lists,
|
||||
|
@ -129,7 +137,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
top() {
|
||||
scroll(this.$el, 0);
|
||||
scroll(this.$el, { top: 0 });
|
||||
},
|
||||
|
||||
async chooseList(ev) {
|
||||
|
|
|
@ -89,7 +89,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
top() {
|
||||
scroll(this.$el, 0);
|
||||
scroll(this.$el, { top: 0 });
|
||||
},
|
||||
|
||||
settings() {
|
||||
|
|
|
@ -1,98 +1,117 @@
|
|||
<template>
|
||||
<transition name="fade" mode="out-in">
|
||||
<div class="ftskorzw wide" v-if="user && narrow === false">
|
||||
<MkRemoteCaution v-if="user.host != null" :href="user.url"/>
|
||||
<div>
|
||||
<MkHeader :info="header"/>
|
||||
<transition name="fade" mode="out-in">
|
||||
<div class="ftskorzw wide" v-if="user && narrow === false">
|
||||
<MkRemoteCaution v-if="user.host != null" :href="user.url"/>
|
||||
|
||||
<div class="banner-container" :style="style">
|
||||
<div class="banner" ref="banner" :style="style"></div>
|
||||
</div>
|
||||
<div class="contents">
|
||||
<div class="side _forceContainerFull_">
|
||||
<MkAvatar class="avatar" :user="user" :disable-preview="true" :show-indicator="true"/>
|
||||
<div class="name">
|
||||
<MkUserName :user="user" :nowrap="false" class="name"/>
|
||||
<MkAcct :user="user" :detail="true" class="acct"/>
|
||||
</div>
|
||||
<div class="followed" v-if="$i && $i.id != user.id && user.isFollowed"><span>{{ $ts.followsYou }}</span></div>
|
||||
<div class="status">
|
||||
<MkA :to="userPage(user)" :class="{ active: page === 'index' }">
|
||||
<b>{{ number(user.notesCount) }}</b>
|
||||
<span>{{ $ts.notes }}</span>
|
||||
</MkA>
|
||||
<MkA :to="userPage(user, 'following')" :class="{ active: page === 'following' }">
|
||||
<b>{{ number(user.followingCount) }}</b>
|
||||
<span>{{ $ts.following }}</span>
|
||||
</MkA>
|
||||
<MkA :to="userPage(user, 'followers')" :class="{ active: page === 'followers' }">
|
||||
<b>{{ number(user.followersCount) }}</b>
|
||||
<span>{{ $ts.followers }}</span>
|
||||
</MkA>
|
||||
</div>
|
||||
<div class="description">
|
||||
<Mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$i" :custom-emojis="user.emojis"/>
|
||||
<p v-else class="empty">{{ $ts.noAccountDescription }}</p>
|
||||
</div>
|
||||
<div class="fields system">
|
||||
<dl class="field" v-if="user.location">
|
||||
<dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ $ts.location }}</dt>
|
||||
<dd class="value">{{ user.location }}</dd>
|
||||
</dl>
|
||||
<dl class="field" v-if="user.birthday">
|
||||
<dt class="name"><i class="fas fa-birthday-cake fa-fw"></i> {{ $ts.birthday }}</dt>
|
||||
<dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd>
|
||||
</dl>
|
||||
<dl class="field">
|
||||
<dt class="name"><i class="fas fa-calendar-alt fa-fw"></i> {{ $ts.registeredDate }}</dt>
|
||||
<dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<MkTime :time="user.createdAt"/>)</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="fields" v-if="user.fields.length > 0">
|
||||
<dl class="field" v-for="(field, i) in user.fields" :key="i">
|
||||
<dt class="name">
|
||||
<Mfm :text="field.name" :plain="true" :custom-emojis="user.emojis" :colored="false"/>
|
||||
</dt>
|
||||
<dd class="value">
|
||||
<Mfm :text="field.value" :author="user" :i="$i" :custom-emojis="user.emojis" :colored="false"/>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<XActivity :user="user" :key="user.id" class="_gap"/>
|
||||
<XPhotos :user="user" :key="user.id" class="_gap"/>
|
||||
<div class="banner-container" :style="style">
|
||||
<div class="banner" ref="banner" :style="style"></div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="actions">
|
||||
<button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button>
|
||||
<MkFollowButton v-if="!$i || $i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" large class="koudoku"/>
|
||||
<div class="contents">
|
||||
<div class="side _forceContainerFull_">
|
||||
<MkAvatar class="avatar" :user="user" :disable-preview="true" :show-indicator="true"/>
|
||||
<div class="name">
|
||||
<MkUserName :user="user" :nowrap="false" class="name"/>
|
||||
<MkAcct :user="user" :detail="true" class="acct"/>
|
||||
</div>
|
||||
<div class="followed" v-if="$i && $i.id != user.id && user.isFollowed"><span>{{ $ts.followsYou }}</span></div>
|
||||
<div class="status">
|
||||
<MkA :to="userPage(user)" :class="{ active: page === 'index' }">
|
||||
<b>{{ number(user.notesCount) }}</b>
|
||||
<span>{{ $ts.notes }}</span>
|
||||
</MkA>
|
||||
<MkA :to="userPage(user, 'following')" :class="{ active: page === 'following' }">
|
||||
<b>{{ number(user.followingCount) }}</b>
|
||||
<span>{{ $ts.following }}</span>
|
||||
</MkA>
|
||||
<MkA :to="userPage(user, 'followers')" :class="{ active: page === 'followers' }">
|
||||
<b>{{ number(user.followersCount) }}</b>
|
||||
<span>{{ $ts.followers }}</span>
|
||||
</MkA>
|
||||
</div>
|
||||
<div class="description">
|
||||
<Mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$i" :custom-emojis="user.emojis"/>
|
||||
<p v-else class="empty">{{ $ts.noAccountDescription }}</p>
|
||||
</div>
|
||||
<div class="fields system">
|
||||
<dl class="field" v-if="user.location">
|
||||
<dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ $ts.location }}</dt>
|
||||
<dd class="value">{{ user.location }}</dd>
|
||||
</dl>
|
||||
<dl class="field" v-if="user.birthday">
|
||||
<dt class="name"><i class="fas fa-birthday-cake fa-fw"></i> {{ $ts.birthday }}</dt>
|
||||
<dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd>
|
||||
</dl>
|
||||
<dl class="field">
|
||||
<dt class="name"><i class="fas fa-calendar-alt fa-fw"></i> {{ $ts.registeredDate }}</dt>
|
||||
<dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<MkTime :time="user.createdAt"/>)</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="fields" v-if="user.fields.length > 0">
|
||||
<dl class="field" v-for="(field, i) in user.fields" :key="i">
|
||||
<dt class="name">
|
||||
<Mfm :text="field.name" :plain="true" :custom-emojis="user.emojis" :colored="false"/>
|
||||
</dt>
|
||||
<dd class="value">
|
||||
<Mfm :text="field.value" :author="user" :i="$i" :custom-emojis="user.emojis" :colored="false"/>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<XActivity :user="user" :key="user.id" class="_gap"/>
|
||||
<XPhotos :user="user" :key="user.id" class="_gap"/>
|
||||
</div>
|
||||
<template v-if="page === 'index'">
|
||||
<div v-if="user.pinnedNotes.length > 0" class="_gap">
|
||||
<XNote v-for="note in user.pinnedNotes" class="note _gap" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/>
|
||||
<div class="main">
|
||||
<div class="actions">
|
||||
<button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button>
|
||||
<MkFollowButton v-if="!$i || $i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" large class="koudoku"/>
|
||||
</div>
|
||||
<div class="_gap">
|
||||
<XUserTimeline :user="user"/>
|
||||
</div>
|
||||
</template>
|
||||
<XFollowList v-else-if="page === 'following'" type="following" :user="user" class="_gap"/>
|
||||
<XFollowList v-else-if="page === 'followers'" type="followers" :user="user" class="_gap"/>
|
||||
<XClips v-else-if="page === 'clips'" :user="user" class="_gap"/>
|
||||
<XPages v-else-if="page === 'pages'" :user="user" class="_gap"/>
|
||||
<template v-if="page === 'index'">
|
||||
<div v-if="user.pinnedNotes.length > 0" class="_gap">
|
||||
<XNote v-for="note in user.pinnedNotes" class="note _gap" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/>
|
||||
</div>
|
||||
<div class="_gap">
|
||||
<XUserTimeline :user="user"/>
|
||||
</div>
|
||||
</template>
|
||||
<XFollowList v-else-if="page === 'following'" type="following" :user="user" class="_gap"/>
|
||||
<XFollowList v-else-if="page === 'followers'" type="followers" :user="user" class="_gap"/>
|
||||
<XClips v-else-if="page === 'clips'" :user="user" class="_gap"/>
|
||||
<XPages v-else-if="page === 'pages'" :user="user" class="_gap"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ftskorzw narrow _root" v-else-if="user && narrow === true" v-size="{ max: [500] }">
|
||||
<!-- TODO -->
|
||||
<!-- <div class="punished" v-if="user.isSuspended"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ $ts.userSuspended }}</div> -->
|
||||
<!-- <div class="punished" v-if="user.isSilenced"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ $ts.userSilenced }}</div> -->
|
||||
<div class="ftskorzw narrow _root" v-else-if="user && narrow === true" v-size="{ max: [500] }">
|
||||
<!-- TODO -->
|
||||
<!-- <div class="punished" v-if="user.isSuspended"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ $ts.userSuspended }}</div> -->
|
||||
<!-- <div class="punished" v-if="user.isSilenced"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ $ts.userSilenced }}</div> -->
|
||||
|
||||
<div class="profile">
|
||||
<MkRemoteCaution v-if="user.host != null" :href="user.url" class="warn"/>
|
||||
<div class="profile">
|
||||
<MkRemoteCaution v-if="user.host != null" :href="user.url" class="warn"/>
|
||||
|
||||
<div class="_block main" :key="user.id">
|
||||
<div class="banner-container" :style="style">
|
||||
<div class="banner" ref="banner" :style="style"></div>
|
||||
<div class="fade"></div>
|
||||
<div class="_block main" :key="user.id">
|
||||
<div class="banner-container" :style="style">
|
||||
<div class="banner" ref="banner" :style="style"></div>
|
||||
<div class="fade"></div>
|
||||
<div class="title">
|
||||
<MkUserName class="name" :user="user" :nowrap="true"/>
|
||||
<div class="bottom">
|
||||
<span class="username"><MkAcct :user="user" :detail="true" /></span>
|
||||
<span v-if="user.isAdmin" :title="$ts.isAdmin" style="color: var(--badge);"><i class="fas fa-bookmark"></i></span>
|
||||
<span v-if="!user.isAdmin && user.isModerator" :title="$ts.isModerator" style="color: var(--badge);"><i class="far fa-bookmark"></i></span>
|
||||
<span v-if="user.isLocked" :title="$ts.isLocked"><i class="fas fa-lock"></i></span>
|
||||
<span v-if="user.isBot" :title="$ts.isBot"><i class="fas fa-robot"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="followed" v-if="$i && $i.id != user.id && user.isFollowed">{{ $ts.followsYou }}</span>
|
||||
<div class="actions" v-if="$i">
|
||||
<button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button>
|
||||
<MkFollowButton v-if="$i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/>
|
||||
</div>
|
||||
</div>
|
||||
<MkAvatar class="avatar" :user="user" :disable-preview="true" :show-indicator="true"/>
|
||||
<div class="title">
|
||||
<MkUserName class="name" :user="user" :nowrap="true"/>
|
||||
<MkUserName :user="user" :nowrap="false" class="name"/>
|
||||
<div class="bottom">
|
||||
<span class="username"><MkAcct :user="user" :detail="true" /></span>
|
||||
<span v-if="user.isAdmin" :title="$ts.isAdmin" style="color: var(--badge);"><i class="fas fa-bookmark"></i></span>
|
||||
|
@ -101,92 +120,76 @@
|
|||
<span v-if="user.isBot" :title="$ts.isBot"><i class="fas fa-robot"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="followed" v-if="$i && $i.id != user.id && user.isFollowed">{{ $ts.followsYou }}</span>
|
||||
<div class="actions" v-if="$i">
|
||||
<button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button>
|
||||
<MkFollowButton v-if="$i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/>
|
||||
<div class="description">
|
||||
<Mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$i" :custom-emojis="user.emojis"/>
|
||||
<p v-else class="empty">{{ $ts.noAccountDescription }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<MkAvatar class="avatar" :user="user" :disable-preview="true" :show-indicator="true"/>
|
||||
<div class="title">
|
||||
<MkUserName :user="user" :nowrap="false" class="name"/>
|
||||
<div class="bottom">
|
||||
<span class="username"><MkAcct :user="user" :detail="true" /></span>
|
||||
<span v-if="user.isAdmin" :title="$ts.isAdmin" style="color: var(--badge);"><i class="fas fa-bookmark"></i></span>
|
||||
<span v-if="!user.isAdmin && user.isModerator" :title="$ts.isModerator" style="color: var(--badge);"><i class="far fa-bookmark"></i></span>
|
||||
<span v-if="user.isLocked" :title="$ts.isLocked"><i class="fas fa-lock"></i></span>
|
||||
<span v-if="user.isBot" :title="$ts.isBot"><i class="fas fa-robot"></i></span>
|
||||
<div class="fields system">
|
||||
<dl class="field" v-if="user.location">
|
||||
<dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ $ts.location }}</dt>
|
||||
<dd class="value">{{ user.location }}</dd>
|
||||
</dl>
|
||||
<dl class="field" v-if="user.birthday">
|
||||
<dt class="name"><i class="fas fa-birthday-cake fa-fw"></i> {{ $ts.birthday }}</dt>
|
||||
<dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd>
|
||||
</dl>
|
||||
<dl class="field">
|
||||
<dt class="name"><i class="fas fa-calendar-alt fa-fw"></i> {{ $ts.registeredDate }}</dt>
|
||||
<dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<MkTime :time="user.createdAt"/>)</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="fields" v-if="user.fields.length > 0">
|
||||
<dl class="field" v-for="(field, i) in user.fields" :key="i">
|
||||
<dt class="name">
|
||||
<Mfm :text="field.name" :plain="true" :custom-emojis="user.emojis" :colored="false"/>
|
||||
</dt>
|
||||
<dd class="value">
|
||||
<Mfm :text="field.value" :author="user" :i="$i" :custom-emojis="user.emojis" :colored="false"/>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="status">
|
||||
<MkA :to="userPage(user)" :class="{ active: page === 'index' }" v-click-anime>
|
||||
<b>{{ number(user.notesCount) }}</b>
|
||||
<span>{{ $ts.notes }}</span>
|
||||
</MkA>
|
||||
<MkA :to="userPage(user, 'following')" :class="{ active: page === 'following' }" v-click-anime>
|
||||
<b>{{ number(user.followingCount) }}</b>
|
||||
<span>{{ $ts.following }}</span>
|
||||
</MkA>
|
||||
<MkA :to="userPage(user, 'followers')" :class="{ active: page === 'followers' }" v-click-anime>
|
||||
<b>{{ number(user.followersCount) }}</b>
|
||||
<span>{{ $ts.followers }}</span>
|
||||
</MkA>
|
||||
</div>
|
||||
</div>
|
||||
<div class="description">
|
||||
<Mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$i" :custom-emojis="user.emojis"/>
|
||||
<p v-else class="empty">{{ $ts.noAccountDescription }}</p>
|
||||
</div>
|
||||
<div class="fields system">
|
||||
<dl class="field" v-if="user.location">
|
||||
<dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ $ts.location }}</dt>
|
||||
<dd class="value">{{ user.location }}</dd>
|
||||
</dl>
|
||||
<dl class="field" v-if="user.birthday">
|
||||
<dt class="name"><i class="fas fa-birthday-cake fa-fw"></i> {{ $ts.birthday }}</dt>
|
||||
<dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd>
|
||||
</dl>
|
||||
<dl class="field">
|
||||
<dt class="name"><i class="fas fa-calendar-alt fa-fw"></i> {{ $ts.registeredDate }}</dt>
|
||||
<dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<MkTime :time="user.createdAt"/>)</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="fields" v-if="user.fields.length > 0">
|
||||
<dl class="field" v-for="(field, i) in user.fields" :key="i">
|
||||
<dt class="name">
|
||||
<Mfm :text="field.name" :plain="true" :custom-emojis="user.emojis" :colored="false"/>
|
||||
</dt>
|
||||
<dd class="value">
|
||||
<Mfm :text="field.value" :author="user" :i="$i" :custom-emojis="user.emojis" :colored="false"/>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="status">
|
||||
<MkA :to="userPage(user)" :class="{ active: page === 'index' }" v-click-anime>
|
||||
<b>{{ number(user.notesCount) }}</b>
|
||||
<span>{{ $ts.notes }}</span>
|
||||
</MkA>
|
||||
<MkA :to="userPage(user, 'following')" :class="{ active: page === 'following' }" v-click-anime>
|
||||
<b>{{ number(user.followingCount) }}</b>
|
||||
<span>{{ $ts.following }}</span>
|
||||
</MkA>
|
||||
<MkA :to="userPage(user, 'followers')" :class="{ active: page === 'followers' }" v-click-anime>
|
||||
<b>{{ number(user.followersCount) }}</b>
|
||||
<span>{{ $ts.followers }}</span>
|
||||
</MkA>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="contents">
|
||||
<template v-if="page === 'index'">
|
||||
<div>
|
||||
<div v-if="user.pinnedNotes.length > 0" class="_gap">
|
||||
<XNote v-for="note in user.pinnedNotes" class="note _block" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/>
|
||||
<div class="contents">
|
||||
<template v-if="page === 'index'">
|
||||
<div>
|
||||
<div v-if="user.pinnedNotes.length > 0" class="_gap">
|
||||
<XNote v-for="note in user.pinnedNotes" class="note _block" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/>
|
||||
</div>
|
||||
<MkInfo v-else-if="$i && $i.id === user.id">{{ $ts.userPagePinTip }}</MkInfo>
|
||||
<XPhotos :user="user" :key="user.id"/>
|
||||
<XActivity :user="user" :key="user.id"/>
|
||||
</div>
|
||||
<MkInfo v-else-if="$i && $i.id === user.id">{{ $ts.userPagePinTip }}</MkInfo>
|
||||
<XPhotos :user="user" :key="user.id"/>
|
||||
<XActivity :user="user" :key="user.id"/>
|
||||
</div>
|
||||
<div>
|
||||
<XUserTimeline :user="user"/>
|
||||
</div>
|
||||
</template>
|
||||
<XFollowList v-else-if="page === 'following'" type="following" :user="user" class="_content _gap"/>
|
||||
<XFollowList v-else-if="page === 'followers'" type="followers" :user="user" class="_content _gap"/>
|
||||
<XClips v-else-if="page === 'clips'" :user="user" class="_gap"/>
|
||||
<XPages v-else-if="page === 'pages'" :user="user" class="_gap"/>
|
||||
<XGallery v-else-if="page === 'gallery'" :user="user" class="_gap"/>
|
||||
<div>
|
||||
<XUserTimeline :user="user"/>
|
||||
</div>
|
||||
</template>
|
||||
<XFollowList v-else-if="page === 'following'" type="following" :user="user" class="_content _gap"/>
|
||||
<XFollowList v-else-if="page === 'followers'" type="followers" :user="user" class="_content _gap"/>
|
||||
<XClips v-else-if="page === 'clips'" :user="user" class="_gap"/>
|
||||
<XPages v-else-if="page === 'pages'" :user="user" class="_gap"/>
|
||||
<XGallery v-else-if="page === 'gallery'" :user="user" class="_gap"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MkError v-else-if="error" @retry="fetch()"/>
|
||||
<MkLoading v-else/>
|
||||
</transition>
|
||||
<MkError v-else-if="error" @retry="fetch()"/>
|
||||
<MkLoading v-else/>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
@ -242,6 +245,15 @@ export default defineComponent({
|
|||
data() {
|
||||
return {
|
||||
[symbols.PAGE_INFO]: computed(() => this.user ? {
|
||||
icon: 'fas fa-user',
|
||||
title: this.user.name ? `${this.user.name} (@${this.user.username})` : `@${this.user.username}`,
|
||||
path: `/@${this.user.username}`,
|
||||
share: {
|
||||
title: this.user.name,
|
||||
},
|
||||
bg: 'var(--bg)',
|
||||
} : null),
|
||||
header: computed(() => this.user ? {
|
||||
title: this.user.name ? `${this.user.name} (@${this.user.username})` : `@${this.user.username}`,
|
||||
subtitle: `@${getAcct(this.user)}`,
|
||||
userName: this.user,
|
||||
|
|
|
@ -23,6 +23,7 @@ const defaultRoutes = [
|
|||
{ path: '/@:acct/room', props: true, component: page('room/room') },
|
||||
{ path: '/settings/:page(.*)?', name: 'settings', component: page('settings/index'), props: route => ({ initialPage: route.params.page || null }) },
|
||||
{ path: '/reset-password/:token?', component: page('reset-password'), props: route => ({ token: route.params.token }) },
|
||||
{ path: '/signup-complete/:code', component: page('signup-complete'), props: route => ({ code: route.params.code }) },
|
||||
{ path: '/announcements', component: page('announcements') },
|
||||
{ path: '/about', component: page('about') },
|
||||
{ path: '/about-misskey', component: page('about-misskey') },
|
||||
|
|
|
@ -4,7 +4,6 @@ import {
|
|||
get as iget,
|
||||
set as iset,
|
||||
del as idel,
|
||||
createStore,
|
||||
} from 'idb-keyval';
|
||||
|
||||
const fallbackName = (key: string) => `idbfallback::${key}`;
|
||||
|
@ -13,9 +12,9 @@ let idbAvailable = typeof window !== 'undefined' ? !!window.indexedDB : true;
|
|||
|
||||
if (idbAvailable) {
|
||||
try {
|
||||
await createStore('keyval-store', 'keyval');
|
||||
await iset('idb-test', 'test');
|
||||
} catch (e) {
|
||||
console.error('idb open error', e);
|
||||
console.error('idb error', e);
|
||||
idbAvailable = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
type ScrollBehavior = 'auto' | 'smooth' | 'instant';
|
||||
|
||||
export function getScrollContainer(el: Element | null): Element | null {
|
||||
if (el == null || el.tagName === 'BODY') return null;
|
||||
const overflow = window.getComputedStyle(el).getPropertyValue('overflow');
|
||||
|
@ -45,21 +47,25 @@ export function onScrollBottom(el: Element, cb) {
|
|||
container.addEventListener('scroll', onScroll, { passive: true });
|
||||
}
|
||||
|
||||
export function scroll(el: Element, top: number) {
|
||||
export function scroll(el: Element, options: {
|
||||
top?: number;
|
||||
left?: number;
|
||||
behavior?: ScrollBehavior;
|
||||
}) {
|
||||
const container = getScrollContainer(el);
|
||||
if (container == null) {
|
||||
window.scroll({ top: top, behavior: 'instant' });
|
||||
window.scroll(options);
|
||||
} else {
|
||||
container.scrollTop = top;
|
||||
container.scroll(options);
|
||||
}
|
||||
}
|
||||
|
||||
export function scrollToTop(el: Element) {
|
||||
scroll(el, 0);
|
||||
export function scrollToTop(el: Element, options: { behavior?: ScrollBehavior; } = {}) {
|
||||
scroll(el, { top: 0, ...options });
|
||||
}
|
||||
|
||||
export function scrollToBottom(el: Element) {
|
||||
scroll(el, 99999); // TODO: ちゃんと計算する
|
||||
export function scrollToBottom(el: Element, options: { behavior?: ScrollBehavior; } = {}) {
|
||||
scroll(el, { top: 99999, ...options }); // TODO: ちゃんと計算する
|
||||
}
|
||||
|
||||
export function isBottom(el: Element, asobi = 0) {
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
|
||||
<main class="main" @contextmenu.stop="onContextmenu">
|
||||
<header class="header">
|
||||
<XHeader class="header" :info="pageInfo" :menu="menu" :center="false" :back-button="true" @back="back()" @click="onHeaderClick"/>
|
||||
<MkHeader class="header" :info="pageInfo" :menu="menu" :center="false" @click="onHeaderClick"/>
|
||||
</header>
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition :name="$store.state.animation ? 'page' : ''" mode="out-in" @enter="onTransition">
|
||||
|
@ -101,7 +101,6 @@ import XSidebar from '@client/ui/_common_/sidebar.vue';
|
|||
import XWidgets from './widgets.vue';
|
||||
import XCommon from '../_common_/common.vue';
|
||||
import XSide from './side.vue';
|
||||
import XHeader from '../_common_/header.vue';
|
||||
import XHeaderClock from './header-clock.vue';
|
||||
import * as os from '@client/os';
|
||||
import { router } from '@client/router';
|
||||
|
@ -117,7 +116,6 @@ export default defineComponent({
|
|||
XSidebar,
|
||||
XWidgets,
|
||||
XSide, // NOTE: dynamic importするとAsyncComponentWrapperが間に入るせいでref取得できなくて面倒になる
|
||||
XHeader,
|
||||
XHeaderClock,
|
||||
},
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="mrajymqm _narrow_" v-if="component">
|
||||
<header class="header" @contextmenu.prevent.stop="onContextmenu">
|
||||
<XHeader class="title" :info="pageInfo" :center="false" :back-button="history.length > 0" @back="back()" :close-button="true" @close="close()"/>
|
||||
<MkHeader class="title" :info="pageInfo" :center="false"/>
|
||||
</header>
|
||||
<component :is="component" v-bind="props" :ref="changePage" class="body _flat_"/>
|
||||
</div>
|
||||
|
@ -9,7 +9,6 @@
|
|||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import XHeader from '../_common_/header.vue';
|
||||
import * as os from '@client/os';
|
||||
import copyToClipboard from '@client/scripts/copy-to-clipboard';
|
||||
import { resolve } from '@client/router';
|
||||
|
@ -18,7 +17,6 @@ import * as symbols from '@client/symbols';
|
|||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XHeader
|
||||
},
|
||||
|
||||
provide() {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
<template>
|
||||
<XColumn v-if="deckStore.state.alwaysShowMainColumn || $route.name !== 'index'" :column="column" :is-stacked="isStacked">
|
||||
<template #header>
|
||||
<XHeader :info="pageInfo" :back-button="true" @back="back()"/>
|
||||
<template v-if="pageInfo">
|
||||
{{ pageInfo.title }}
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<router-view v-slot="{ Component }" class="_flat_">
|
||||
|
@ -18,7 +20,6 @@
|
|||
import { defineComponent } from 'vue';
|
||||
import XColumn from './column.vue';
|
||||
import XNotes from '@client/components/notes.vue';
|
||||
import XHeader from '@client/ui/_common_/header.vue';
|
||||
import { deckStore } from '@client/ui/deck/deck-store';
|
||||
import * as os from '@client/os';
|
||||
import * as symbols from '@client/symbols';
|
||||
|
@ -26,7 +27,6 @@ import * as symbols from '@client/symbols';
|
|||
export default defineComponent({
|
||||
components: {
|
||||
XColumn,
|
||||
XHeader,
|
||||
XNotes
|
||||
},
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<MkAvatar :user="$i" class="avatar"/><MkAcct class="acct" :user="$i"/>
|
||||
</button>
|
||||
<div class="post" @click="post">
|
||||
<MkButton class="button" primary full>
|
||||
<MkButton class="button" primary full rounded>
|
||||
<i class="fas fa-pencil-alt fa-fw"></i>
|
||||
</MkButton>
|
||||
</div>
|
||||
|
|
|
@ -4,9 +4,10 @@
|
|||
<header class="header" @contextmenu.prevent.stop="onContextmenu">
|
||||
<button class="_button" @click="back()" v-if="history.length > 0"><i class="fas fa-chevron-left"></i></button>
|
||||
<button class="_button" style="pointer-events: none;" v-else><!-- マージンのバランスを取るためのダミー --></button>
|
||||
<XHeader class="title" :info="pageInfo" :back-button="false"/>
|
||||
<span class="title">{{ pageInfo.title }}</span>
|
||||
<button class="_button" @click="close()"><i class="fas fa-times"></i></button>
|
||||
</header>
|
||||
<MkHeader class="pageHeader" :info="pageInfo"/>
|
||||
<component :is="component" v-bind="props" :ref="changePage"/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -14,7 +15,6 @@
|
|||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import XHeader from './_common_/header.vue';
|
||||
import * as os from '@client/os';
|
||||
import copyToClipboard from '@client/scripts/copy-to-clipboard';
|
||||
import { resolve } from '@client/router';
|
||||
|
@ -22,10 +22,6 @@ import { url } from '@client/config';
|
|||
import * as symbols from '@client/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XHeader
|
||||
},
|
||||
|
||||
provide() {
|
||||
return {
|
||||
navHook: (path) => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="mk-app" :class="{ wallpaper, isMobile }">
|
||||
<XHeaderMenu v-if="showMenuOnTop"/>
|
||||
<div class="mk-app" :class="{ wallpaper, isMobile }" :style="`--globalHeaderHeight:${globalHeaderHeight}px`">
|
||||
<XHeaderMenu v-if="showMenuOnTop" v-get-size="(w, h) => globalHeaderHeight = h"/>
|
||||
|
||||
<div class="columns" :class="{ fullView, withGlobalHeader: showMenuOnTop }">
|
||||
<template v-if="!isMobile">
|
||||
|
@ -13,9 +13,6 @@
|
|||
</template>
|
||||
|
||||
<main class="main" @contextmenu.stop="onContextmenu" :style="{ background: pageInfo?.bg }">
|
||||
<header class="header" @click="onHeaderClick">
|
||||
<XHeader :info="pageInfo" :back-button="true" @back="back()"/>
|
||||
</header>
|
||||
<div class="content" :class="{ _flat_: !fullView }">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition :name="$store.state.animation ? 'page' : ''" mode="out-in" @enter="onTransition">
|
||||
|
@ -67,7 +64,6 @@ import { StickySidebar } from '@client/scripts/sticky-sidebar';
|
|||
import XSidebar from './default.sidebar.vue';
|
||||
import XDrawerSidebar from '@client/ui/_common_/sidebar.vue';
|
||||
import XCommon from './_common_/common.vue';
|
||||
import XHeader from './_common_/header.vue';
|
||||
import * as os from '@client/os';
|
||||
import { menuDef } from '@client/menu';
|
||||
import * as symbols from '@client/symbols';
|
||||
|
@ -80,7 +76,6 @@ export default defineComponent({
|
|||
XCommon,
|
||||
XSidebar,
|
||||
XDrawerSidebar,
|
||||
XHeader,
|
||||
XHeaderMenu: defineAsyncComponent(() => import('./default.header.vue')),
|
||||
XWidgets: defineAsyncComponent(() => import('./default.widgets.vue')),
|
||||
},
|
||||
|
@ -89,6 +84,7 @@ export default defineComponent({
|
|||
return {
|
||||
pageInfo: null,
|
||||
menuDef: menuDef,
|
||||
globalHeaderHeight: 0,
|
||||
isMobile: window.innerWidth <= MOBILE_THRESHOLD,
|
||||
isDesktop: window.innerWidth >= DESKTOP_THRESHOLD,
|
||||
widgetsShowing: false,
|
||||
|
@ -193,10 +189,6 @@ export default defineComponent({
|
|||
if (window._scroll) window._scroll();
|
||||
},
|
||||
|
||||
onHeaderClick() {
|
||||
window.scroll({ top: 0, behavior: 'smooth' });
|
||||
},
|
||||
|
||||
onContextmenu(e) {
|
||||
const isLink = (el: HTMLElement) => {
|
||||
if (el.tagName === 'A') return true;
|
||||
|
@ -257,7 +249,6 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
.mk-app {
|
||||
$header-height: 50px;
|
||||
$ui-font-size: 1em;
|
||||
$widgets-hide-threshold: 1200px;
|
||||
$nav-icon-only-width: 78px; // TODO: どこかに集約したい
|
||||
|
@ -282,10 +273,6 @@ export default defineComponent({
|
|||
border: none;
|
||||
width: 100%;
|
||||
border-radius: 0;
|
||||
|
||||
> .header {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -325,30 +312,6 @@ export default defineComponent({
|
|||
border-radius: 0;
|
||||
overflow: clip;
|
||||
--margin: 12px;
|
||||
|
||||
> .header {
|
||||
position: sticky;
|
||||
z-index: 1000;
|
||||
top: var(--globalHeaderHeight, 0px);
|
||||
height: $header-height;
|
||||
-webkit-backdrop-filter: var(--blur, blur(32px));
|
||||
backdrop-filter: var(--blur, blur(32px));
|
||||
background-color: var(--header);
|
||||
border-bottom: solid 0.5px var(--divider);
|
||||
}
|
||||
|
||||
> .content {
|
||||
--stickyTop: calc(var(--globalHeaderHeight, 0px) + #{$header-height});
|
||||
}
|
||||
|
||||
@media (max-width: 850px) {
|
||||
padding-top: $header-height;
|
||||
|
||||
> .header {
|
||||
position: fixed;
|
||||
width: calc(100% - #{$nav-icon-only-width});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .widgets {
|
||||
|
@ -370,12 +333,11 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
&.withGlobalHeader {
|
||||
--globalHeaderHeight: 60px; // TODO: 60pxと決め打ちしているのを直す
|
||||
|
||||
> .main {
|
||||
margin-top: 0;
|
||||
border: solid 1px var(--divider);
|
||||
border-radius: var(--radius);
|
||||
--stickyTop: var(--globalHeaderHeight);
|
||||
}
|
||||
|
||||
> .widgets {
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
<XSidebar ref="nav" class="sidebar"/>
|
||||
|
||||
<div class="contents" ref="contents" @contextmenu.stop="onContextmenu" :style="{ background: pageInfo?.bg }">
|
||||
<header class="header" ref="header" @click="onHeaderClick" :style="{ background: pageInfo?.bg }">
|
||||
<XHeader :info="pageInfo" :back-button="true" @back="back()"/>
|
||||
</header>
|
||||
<main ref="main">
|
||||
<div class="content">
|
||||
<router-view v-slot="{ Component }">
|
||||
|
@ -58,7 +55,6 @@ import { instanceName } from '@client/config';
|
|||
import { StickySidebar } from '@client/scripts/sticky-sidebar';
|
||||
import XSidebar from '@client/ui/_common_/sidebar.vue';
|
||||
import XCommon from './_common_/common.vue';
|
||||
import XHeader from './_common_/header.vue';
|
||||
import XSide from './default.side.vue';
|
||||
import * as os from '@client/os';
|
||||
import { menuDef } from '@client/menu';
|
||||
|
@ -70,7 +66,6 @@ export default defineComponent({
|
|||
components: {
|
||||
XCommon,
|
||||
XSidebar,
|
||||
XHeader,
|
||||
XWidgets: defineAsyncComponent(() => import('./universal.widgets.vue')),
|
||||
XSide, // NOTE: dynamic importするとAsyncComponentWrapperが間に入るせいでref取得できなくて面倒になる
|
||||
},
|
||||
|
@ -151,9 +146,6 @@ export default defineComponent({
|
|||
adjustUI() {
|
||||
const navWidth = this.$refs.nav.$el.offsetWidth;
|
||||
this.navHidden = navWidth === 0;
|
||||
if (this.$refs.contents == null) return;
|
||||
const width = this.$refs.contents.offsetWidth;
|
||||
if (this.$refs.header) this.$refs.header.style.width = `${width}px`;
|
||||
},
|
||||
|
||||
showNav() {
|
||||
|
@ -183,10 +175,6 @@ export default defineComponent({
|
|||
if (window._scroll) window._scroll();
|
||||
},
|
||||
|
||||
onHeaderClick() {
|
||||
window.scroll({ top: 0, behavior: 'smooth' });
|
||||
},
|
||||
|
||||
onContextmenu(e) {
|
||||
const isLink = (el: HTMLElement) => {
|
||||
if (el.tagName === 'A') return true;
|
||||
|
@ -243,7 +231,6 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
.mk-app {
|
||||
$header-height: 58px; // TODO: どこかに集約したい
|
||||
$ui-font-size: 1em; // TODO: どこかに集約したい
|
||||
$widgets-hide-threshold: 1090px;
|
||||
|
||||
|
@ -263,37 +250,11 @@ export default defineComponent({
|
|||
> .contents {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
--stickyTop: #{$header-height};
|
||||
padding-top: $header-height;
|
||||
background: var(--panel);
|
||||
|
||||
> .header {
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
top: 0;
|
||||
height: $header-height;
|
||||
width: 100%;
|
||||
line-height: $header-height;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
//background-color: var(--panel);
|
||||
-webkit-backdrop-filter: var(--blur, blur(32px));
|
||||
backdrop-filter: var(--blur, blur(32px));
|
||||
background-color: var(--header);
|
||||
border-bottom: solid 0.5px var(--divider);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
> main {
|
||||
min-width: 0;
|
||||
|
||||
> .content {
|
||||
> * {
|
||||
// ほんとは単に calc(100vh - #{$header-height}) と書きたいところだが... https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
|
||||
min-height: calc((var(--vh, 1vh) * 100) - #{$header-height});
|
||||
}
|
||||
}
|
||||
|
||||
> .spacer {
|
||||
height: 82px;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class="mk-app">
|
||||
<div class="contents">
|
||||
<header class="header">
|
||||
<XHeader :info="pageInfo"/>
|
||||
<MkHeader :info="pageInfo"/>
|
||||
</header>
|
||||
<main ref="main">
|
||||
<div class="content">
|
||||
|
@ -24,14 +24,12 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent, defineAsyncComponent } from 'vue';
|
||||
import { host } from '@client/config';
|
||||
import XHeader from './_common_/header.vue';
|
||||
import XCommon from './_common_/common.vue';
|
||||
import * as symbols from '@client/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XCommon,
|
||||
XHeader,
|
||||
},
|
||||
|
||||
data() {
|
||||
|
|
|
@ -63,7 +63,7 @@ import { Antenna } from '@/models/entities/antenna';
|
|||
import { AntennaNote } from '@/models/entities/antenna-note';
|
||||
import { PromoNote } from '@/models/entities/promo-note';
|
||||
import { PromoRead } from '@/models/entities/promo-read';
|
||||
import { program } from '../argv';
|
||||
import { envOption } from '../env';
|
||||
import { Relay } from '@/models/entities/relay';
|
||||
import { MutedNote } from '@/models/entities/muted-note';
|
||||
import { Channel } from '@/models/entities/channel';
|
||||
|
@ -72,6 +72,7 @@ import { ChannelNotePining } from '@/models/entities/channel-note-pining';
|
|||
import { RegistryItem } from '@/models/entities/registry-item';
|
||||
import { Ad } from '@/models/entities/ad';
|
||||
import { PasswordResetRequest } from '@/models/entities/password-reset-request';
|
||||
import { UserPending } from '@/models/entities/user-pending';
|
||||
|
||||
const sqlLogger = dbLogger.createSubLogger('sql', 'white', false);
|
||||
|
||||
|
@ -83,7 +84,7 @@ class MyCustomLogger implements Logger {
|
|||
}
|
||||
|
||||
public logQuery(query: string, parameters?: any[]) {
|
||||
if (program.verbose) {
|
||||
if (envOption.verbose) {
|
||||
sqlLogger.info(this.highlight(query));
|
||||
}
|
||||
}
|
||||
|
@ -173,6 +174,7 @@ export const entities = [
|
|||
RegistryItem,
|
||||
Ad,
|
||||
PasswordResetRequest,
|
||||
UserPending,
|
||||
...charts as any
|
||||
];
|
||||
|
||||
|
|
20
src/env.ts
Normal file
20
src/env.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
const envOption = {
|
||||
onlyQueue: false,
|
||||
onlyServer: false,
|
||||
noDaemons: false,
|
||||
disableClustering: false,
|
||||
verbose: false,
|
||||
withLogTime: false,
|
||||
quiet: false,
|
||||
slow: false,
|
||||
};
|
||||
|
||||
for (const key of Object.keys(envOption) as (keyof typeof envOption)[]) {
|
||||
if (process.env['MK_' + key.replace(/[A-Z]/g, letter => `_${letter}`).toUpperCase()]) envOption[key] = true;
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'test') envOption.disableClustering = true;
|
||||
if (process.env.NODE_ENV === 'test') envOption.quiet = true;
|
||||
if (process.env.NODE_ENV === 'test') envOption.noDaemons = true;
|
||||
|
||||
export { envOption };
|
|
@ -148,6 +148,11 @@ export class Meta {
|
|||
@JoinColumn()
|
||||
public proxyAccount: User | null;
|
||||
|
||||
@Column('boolean', {
|
||||
default: false,
|
||||
})
|
||||
public emailRequiredForSignup: boolean;
|
||||
|
||||
@Column('boolean', {
|
||||
default: false,
|
||||
})
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Note } from './note';
|
|||
import { FollowRequest } from './follow-request';
|
||||
import { UserGroupInvitation } from './user-group-invitation';
|
||||
import { AccessToken } from './access-token';
|
||||
import { notificationTypes } from '../../types';
|
||||
import { notificationTypes } from '@/types';
|
||||
|
||||
@Entity()
|
||||
export class Notification {
|
||||
|
|
32
src/models/entities/user-pending.ts
Normal file
32
src/models/entities/user-pending.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { PrimaryColumn, Entity, Index, Column } from 'typeorm';
|
||||
import { id } from '../id';
|
||||
|
||||
@Entity()
|
||||
export class UserPending {
|
||||
@PrimaryColumn(id())
|
||||
public id: string;
|
||||
|
||||
@Column('timestamp with time zone')
|
||||
public createdAt: Date;
|
||||
|
||||
@Index({ unique: true })
|
||||
@Column('varchar', {
|
||||
length: 128,
|
||||
})
|
||||
public code: string;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 128,
|
||||
})
|
||||
public username: string;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 128,
|
||||
})
|
||||
public email: string;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 128,
|
||||
})
|
||||
public password: string;
|
||||
}
|
|
@ -2,7 +2,7 @@ import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'type
|
|||
import { id } from '../id';
|
||||
import { User } from './user';
|
||||
import { Page } from './page';
|
||||
import { notificationTypes } from '../../types';
|
||||
import { notificationTypes } from '@/types';
|
||||
|
||||
// TODO: このテーブルで管理している情報すべてレジストリで管理するようにしても良いかも
|
||||
// ただ、「emailVerified が true なユーザーを find する」のようなクエリは書けなくなるからウーン
|
||||
|
|
|
@ -62,6 +62,7 @@ import { ChannelNotePining } from './entities/channel-note-pining';
|
|||
import { RegistryItem } from './entities/registry-item';
|
||||
import { Ad } from './entities/ad';
|
||||
import { PasswordResetRequest } from './entities/password-reset-request';
|
||||
import { UserPending } from './entities/user-pending';
|
||||
|
||||
export const Announcements = getRepository(Announcement);
|
||||
export const AnnouncementReads = getRepository(AnnouncementRead);
|
||||
|
@ -76,6 +77,7 @@ export const PollVotes = getRepository(PollVote);
|
|||
export const Users = getCustomRepository(UserRepository);
|
||||
export const UserProfiles = getRepository(UserProfile);
|
||||
export const UserKeypairs = getRepository(UserKeypair);
|
||||
export const UserPendings = getRepository(UserPending);
|
||||
export const AttestationChallenges = getRepository(AttestationChallenge);
|
||||
export const UserSecurityKeys = getRepository(UserSecurityKey);
|
||||
export const UserPublickeys = getRepository(UserPublickey);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as httpSignature from 'http-signature';
|
||||
|
||||
import config from '@/config/index';
|
||||
import { program } from '../argv';
|
||||
import { envOption } from '../env';
|
||||
|
||||
import processDeliver from './processors/deliver';
|
||||
import processInbox from './processors/inbox';
|
||||
|
@ -200,7 +200,7 @@ export function createCleanRemoteFilesJob() {
|
|||
}
|
||||
|
||||
export default function() {
|
||||
if (!program.onlyServer) {
|
||||
if (!envOption.onlyServer) {
|
||||
deliverQueue.process(config.deliverJobConcurrency || 128, processDeliver);
|
||||
inboxQueue.process(config.inboxJobConcurrency || 16, processInbox);
|
||||
processDb(dbQueue);
|
||||
|
|
|
@ -11,20 +11,30 @@ import { UserKeypair } from '@/models/entities/user-keypair';
|
|||
import { usersChart } from '@/services/chart/index';
|
||||
import { UsedUsername } from '@/models/entities/used-username';
|
||||
|
||||
export async function signup(username: User['username'], password: UserProfile['password'], host: string | null = null) {
|
||||
export async function signup(opts: {
|
||||
username: User['username'];
|
||||
password?: string | null;
|
||||
passwordHash?: UserProfile['password'] | null;
|
||||
host?: string | null;
|
||||
}) {
|
||||
const { username, password, passwordHash, host } = opts;
|
||||
let hash = passwordHash;
|
||||
|
||||
// Validate username
|
||||
if (!Users.validateLocalUsername.ok(username)) {
|
||||
throw new Error('INVALID_USERNAME');
|
||||
}
|
||||
|
||||
// Validate password
|
||||
if (!Users.validatePassword.ok(password)) {
|
||||
throw new Error('INVALID_PASSWORD');
|
||||
}
|
||||
if (password != null && passwordHash == null) {
|
||||
// Validate password
|
||||
if (!Users.validatePassword.ok(password)) {
|
||||
throw new Error('INVALID_PASSWORD');
|
||||
}
|
||||
|
||||
// Generate hash of password
|
||||
const salt = await bcrypt.genSalt(8);
|
||||
const hash = await bcrypt.hash(password, salt);
|
||||
// Generate hash of password
|
||||
const salt = await bcrypt.genSalt(8);
|
||||
hash = await bcrypt.hash(password, salt);
|
||||
}
|
||||
|
||||
// Generate secret
|
||||
const secret = generateUserToken();
|
||||
|
|
|
@ -35,7 +35,10 @@ export default define(meta, async (ps, _me) => {
|
|||
})) === 0;
|
||||
if (!noUsers && !me?.isAdmin) throw new Error('access denied');
|
||||
|
||||
const { account, secret } = await signup(ps.username, ps.password);
|
||||
const { account, secret } = await signup({
|
||||
username: ps.username,
|
||||
password: ps.password,
|
||||
});
|
||||
|
||||
const res = await Users.pack(account, account, {
|
||||
detail: true,
|
||||
|
|
|
@ -93,6 +93,10 @@ export const meta = {
|
|||
validator: $.optional.bool,
|
||||
},
|
||||
|
||||
emailRequiredForSignup: {
|
||||
validator: $.optional.bool,
|
||||
},
|
||||
|
||||
enableHcaptcha: {
|
||||
validator: $.optional.bool,
|
||||
},
|
||||
|
@ -374,6 +378,10 @@ export default define(meta, async (ps, me) => {
|
|||
set.proxyRemoteFiles = ps.proxyRemoteFiles;
|
||||
}
|
||||
|
||||
if (ps.emailRequiredForSignup !== undefined) {
|
||||
set.emailRequiredForSignup = ps.emailRequiredForSignup;
|
||||
}
|
||||
|
||||
if (ps.enableHcaptcha !== undefined) {
|
||||
set.enableHcaptcha = ps.enableHcaptcha;
|
||||
}
|
||||
|
|
|
@ -2,11 +2,17 @@ import $ from 'cafy';
|
|||
import define from '../../define';
|
||||
import Resolver from '@/remote/activitypub/resolver';
|
||||
import { ApiError } from '../../error';
|
||||
import * as ms from 'ms';
|
||||
|
||||
export const meta = {
|
||||
tags: ['federation'],
|
||||
|
||||
requireCredential: false as const,
|
||||
requireCredential: true as const,
|
||||
|
||||
limit: {
|
||||
duration: ms('1hour'),
|
||||
max: 30
|
||||
},
|
||||
|
||||
params: {
|
||||
uri: {
|
||||
|
|
|
@ -11,11 +11,17 @@ import { Note } from '@/models/entities/note';
|
|||
import { User } from '@/models/entities/user';
|
||||
import { fetchMeta } from '@/misc/fetch-meta';
|
||||
import { isActor, isPost, getApId } from '@/remote/activitypub/type';
|
||||
import * as ms from 'ms';
|
||||
|
||||
export const meta = {
|
||||
tags: ['federation'],
|
||||
|
||||
requireCredential: false as const,
|
||||
requireCredential: true as const,
|
||||
|
||||
limit: {
|
||||
duration: ms('1hour'),
|
||||
max: 30
|
||||
},
|
||||
|
||||
params: {
|
||||
uri: {
|
||||
|
|
37
src/server/api/endpoints/email-address/available.ts
Normal file
37
src/server/api/endpoints/email-address/available.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import $ from 'cafy';
|
||||
import define from '../../define';
|
||||
import { UserProfiles } from '@/models/index';
|
||||
|
||||
export const meta = {
|
||||
tags: ['users'],
|
||||
|
||||
requireCredential: false as const,
|
||||
|
||||
params: {
|
||||
emailAddress: {
|
||||
validator: $.str
|
||||
}
|
||||
},
|
||||
|
||||
res: {
|
||||
type: 'object' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
properties: {
|
||||
available: {
|
||||
type: 'boolean' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default define(meta, async (ps) => {
|
||||
const exist = await UserProfiles.count({
|
||||
emailVerified: true,
|
||||
email: ps.emailAddress,
|
||||
});
|
||||
|
||||
return {
|
||||
available: exist === 0
|
||||
};
|
||||
});
|
|
@ -4,7 +4,7 @@ import { readNotification } from '../../common/read-notification';
|
|||
import define from '../../define';
|
||||
import { makePaginationQuery } from '../../common/make-pagination-query';
|
||||
import { Notifications, Followings, Mutings, Users } from '@/models/index';
|
||||
import { notificationTypes } from '../../../../types';
|
||||
import { notificationTypes } from '@/types';
|
||||
import read from '@/services/note/read';
|
||||
|
||||
export const meta = {
|
||||
|
@ -33,6 +33,11 @@ export const meta = {
|
|||
default: false
|
||||
},
|
||||
|
||||
unreadOnly: {
|
||||
validator: $.optional.bool,
|
||||
default: false
|
||||
},
|
||||
|
||||
markAsRead: {
|
||||
validator: $.optional.bool,
|
||||
default: true
|
||||
|
@ -105,6 +110,10 @@ export default define(meta, async (ps, user) => {
|
|||
query.andWhere(`notification.type NOT IN (:...excludeTypes)`, { excludeTypes: ps.excludeTypes });
|
||||
}
|
||||
|
||||
if (ps.unreadOnly) {
|
||||
query.andWhere(`notification.isRead = false`);
|
||||
}
|
||||
|
||||
const notifications = await query.take(ps.limit!).getMany();
|
||||
|
||||
// Mark all as read
|
||||
|
|
|
@ -13,7 +13,7 @@ import { ApiError } from '../../error';
|
|||
import { Users, DriveFiles, UserProfiles, Pages } from '@/models/index';
|
||||
import { User } from '@/models/entities/user';
|
||||
import { UserProfile } from '@/models/entities/user-profile';
|
||||
import { notificationTypes } from '../../../../types';
|
||||
import { notificationTypes } from '@/types';
|
||||
import { normalizeForSearch } from '@/misc/normalize-for-search';
|
||||
|
||||
export const meta = {
|
||||
|
|
|
@ -104,6 +104,10 @@ export const meta = {
|
|||
type: 'boolean' as const,
|
||||
optional: false as const, nullable: false as const
|
||||
},
|
||||
emailRequiredForSignup: {
|
||||
type: 'boolean' as const,
|
||||
optional: false as const, nullable: false as const
|
||||
},
|
||||
enableHcaptcha: {
|
||||
type: 'boolean' as const,
|
||||
optional: false as const, nullable: false as const
|
||||
|
@ -488,6 +492,7 @@ export default define(meta, async (ps, me) => {
|
|||
disableGlobalTimeline: instance.disableGlobalTimeline,
|
||||
driveCapacityPerLocalUserMb: instance.localDriveCapacityMb,
|
||||
driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb,
|
||||
emailRequiredForSignup: instance.emailRequiredForSignup,
|
||||
enableHcaptcha: instance.enableHcaptcha,
|
||||
hcaptchaSiteKey: instance.hcaptchaSiteKey,
|
||||
enableRecaptcha: instance.enableRecaptcha,
|
||||
|
@ -537,6 +542,7 @@ export default define(meta, async (ps, me) => {
|
|||
registration: !instance.disableRegistration,
|
||||
localTimeLine: !instance.disableLocalTimeline,
|
||||
globalTimeLine: !instance.disableGlobalTimeline,
|
||||
emailRequiredForSignup: instance.emailRequiredForSignup,
|
||||
elasticsearch: config.elasticsearch ? true : false,
|
||||
hcaptcha: instance.enableHcaptcha,
|
||||
recaptcha: instance.enableRecaptcha,
|
||||
|
|
|
@ -12,6 +12,7 @@ import endpoints from './endpoints';
|
|||
import handler from './api-handler';
|
||||
import signup from './private/signup';
|
||||
import signin from './private/signin';
|
||||
import signupPending from './private/signup-pending';
|
||||
import discord from './service/discord';
|
||||
import github from './service/github';
|
||||
import twitter from './service/twitter';
|
||||
|
@ -65,6 +66,7 @@ for (const endpoint of endpoints) {
|
|||
|
||||
router.post('/signup', signup);
|
||||
router.post('/signin', signin);
|
||||
router.post('/signup-pending', signupPending);
|
||||
|
||||
router.use(discord.routes());
|
||||
router.use(github.routes());
|
||||
|
|
35
src/server/api/private/signup-pending.ts
Normal file
35
src/server/api/private/signup-pending.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import * as Koa from 'koa';
|
||||
import { Users, UserPendings, UserProfiles } from '@/models/index';
|
||||
import { signup } from '../common/signup';
|
||||
import signin from '../common/signin';
|
||||
|
||||
export default async (ctx: Koa.Context) => {
|
||||
const body = ctx.request.body;
|
||||
|
||||
const code = body['code'];
|
||||
|
||||
try {
|
||||
const pendingUser = await UserPendings.findOneOrFail({ code });
|
||||
|
||||
const { account, secret } = await signup({
|
||||
username: pendingUser.username,
|
||||
passwordHash: pendingUser.password,
|
||||
});
|
||||
|
||||
UserPendings.delete({
|
||||
id: pendingUser.id,
|
||||
});
|
||||
|
||||
const profile = await UserProfiles.findOneOrFail(account.id);
|
||||
|
||||
await UserProfiles.update({ userId: profile.userId }, {
|
||||
email: pendingUser.email,
|
||||
emailVerified: true,
|
||||
emailVerifyCode: null,
|
||||
});
|
||||
|
||||
signin(ctx, account);
|
||||
} catch (e) {
|
||||
ctx.throw(400, e);
|
||||
}
|
||||
};
|
|
@ -1,8 +1,13 @@
|
|||
import * as Koa from 'koa';
|
||||
import rndstr from 'rndstr';
|
||||
import * as bcrypt from 'bcryptjs';
|
||||
import { fetchMeta } from '@/misc/fetch-meta';
|
||||
import { verifyHcaptcha, verifyRecaptcha } from '@/misc/captcha';
|
||||
import { Users, RegistrationTickets } from '@/models/index';
|
||||
import { Users, RegistrationTickets, UserPendings } from '@/models/index';
|
||||
import { signup } from '../common/signup';
|
||||
import config from '@/config';
|
||||
import { sendEmail } from '@/services/send-email';
|
||||
import { genId } from '@/misc/gen-id';
|
||||
|
||||
export default async (ctx: Koa.Context) => {
|
||||
const body = ctx.request.body;
|
||||
|
@ -29,8 +34,16 @@ export default async (ctx: Koa.Context) => {
|
|||
const password = body['password'];
|
||||
const host: string | null = process.env.NODE_ENV === 'test' ? (body['host'] || null) : null;
|
||||
const invitationCode = body['invitationCode'];
|
||||
const emailAddress = body['emailAddress'];
|
||||
|
||||
if (instance && instance.disableRegistration) {
|
||||
if (instance.emailRequiredForSignup) {
|
||||
if (emailAddress == null || typeof emailAddress != 'string') {
|
||||
ctx.status = 400;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (instance.disableRegistration) {
|
||||
if (invitationCode == null || typeof invitationCode != 'string') {
|
||||
ctx.status = 400;
|
||||
return;
|
||||
|
@ -48,18 +61,45 @@ export default async (ctx: Koa.Context) => {
|
|||
RegistrationTickets.delete(ticket.id);
|
||||
}
|
||||
|
||||
try {
|
||||
const { account, secret } = await signup(username, password, host);
|
||||
if (instance.emailRequiredForSignup) {
|
||||
const code = rndstr('a-z0-9', 16);
|
||||
|
||||
const res = await Users.pack(account, account, {
|
||||
detail: true,
|
||||
includeSecrets: true
|
||||
// Generate hash of password
|
||||
const salt = await bcrypt.genSalt(8);
|
||||
const hash = await bcrypt.hash(password, salt);
|
||||
|
||||
await UserPendings.insert({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
code,
|
||||
email: emailAddress,
|
||||
username: username,
|
||||
password: hash,
|
||||
});
|
||||
|
||||
(res as any).token = secret;
|
||||
const link = `${config.url}/signup-complete/${code}`;
|
||||
|
||||
ctx.body = res;
|
||||
} catch (e) {
|
||||
ctx.throw(400, e);
|
||||
sendEmail(emailAddress, 'Signup',
|
||||
`To complete signup, please click this link:<br><a href="${link}">${link}</a>`,
|
||||
`To complete signup, please click this link: ${link}`);
|
||||
|
||||
ctx.status = 204;
|
||||
} else {
|
||||
try {
|
||||
const { account, secret } = await signup({
|
||||
username, password, host
|
||||
});
|
||||
|
||||
const res = await Users.pack(account, account, {
|
||||
detail: true,
|
||||
includeSecrets: true
|
||||
});
|
||||
|
||||
(res as any).token = secret;
|
||||
|
||||
ctx.body = res;
|
||||
} catch (e) {
|
||||
ctx.throw(400, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@ import config from '@/config/index';
|
|||
import apiServer from './api/index';
|
||||
import { sum } from '@/prelude/array';
|
||||
import Logger from '@/services/logger';
|
||||
import { program } from '../argv';
|
||||
import { envOption } from '../env';
|
||||
import { UserProfiles, Users } from '@/models/index';
|
||||
import { networkChart } from '@/services/chart/index';
|
||||
import { genAvatar } from '@/misc/gen-avatar';
|
||||
|
@ -40,7 +40,7 @@ if (!['production', 'test'].includes(process.env.NODE_ENV || '')) {
|
|||
}));
|
||||
|
||||
// Delay
|
||||
if (program.slow) {
|
||||
if (envOption.slow) {
|
||||
app.use(slow({
|
||||
delay: 3000
|
||||
}));
|
||||
|
|
|
@ -68,6 +68,7 @@ const nodeinfo2 = async () => {
|
|||
disableRegistration: meta.disableRegistration,
|
||||
disableLocalTimeline: meta.disableLocalTimeline,
|
||||
disableGlobalTimeline: meta.disableGlobalTimeline,
|
||||
emailRequiredForSignup: meta.emailRequiredForSignup,
|
||||
enableHcaptcha: meta.enableHcaptcha,
|
||||
enableRecaptcha: meta.enableRecaptcha,
|
||||
maxNoteTextLength: meta.maxNoteTextLength,
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as cluster from 'cluster';
|
|||
import * as os from 'os';
|
||||
import * as chalk from 'chalk';
|
||||
import * as dateformat from 'dateformat';
|
||||
import { program } from '../argv';
|
||||
import { envOption } from '../env';
|
||||
import { getRepository } from 'typeorm';
|
||||
import { Log } from '@/models/entities/log';
|
||||
import { genId } from '@/misc/gen-id';
|
||||
|
@ -52,7 +52,7 @@ export default class Logger {
|
|||
}
|
||||
|
||||
private log(level: Level, message: string, data?: Record<string, any> | null, important = false, subDomains: Domain[] = [], store = true): void {
|
||||
if (program.quiet) return;
|
||||
if (envOption.quiet) return;
|
||||
if (!this.store) store = false;
|
||||
if (level === 'debug') store = false;
|
||||
|
||||
|
@ -80,7 +80,7 @@ export default class Logger {
|
|||
null;
|
||||
|
||||
let log = `${l} ${worker}\t[${domains.join(' ')}]\t${m}`;
|
||||
if (program.withLogTime) log = chalk.gray(time) + ' ' + log;
|
||||
if (envOption.withLogTime) log = chalk.gray(time) + ' ' + log;
|
||||
|
||||
console.log(important ? chalk.bold(log) : log);
|
||||
|
||||
|
@ -132,7 +132,7 @@ export default class Logger {
|
|||
}
|
||||
|
||||
public debug(message: string, data?: Record<string, any> | null, important = false): void { // デバッグ用に使う(開発者に必要だが利用者に不要な情報)
|
||||
if (process.env.NODE_ENV != 'production' || program.verbose) {
|
||||
if (process.env.NODE_ENV != 'production' || envOption.verbose) {
|
||||
this.log('debug', message, data, important);
|
||||
}
|
||||
}
|
||||
|
|
400
yarn.lock
400
yarn.lock
|
@ -447,10 +447,10 @@
|
|||
"@types/connect" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/bull@3.15.4":
|
||||
version "3.15.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/bull/-/bull-3.15.4.tgz#71390e83b6aa41a9106535240ad226c5239615f1"
|
||||
integrity sha512-vsw4kqsI/SLocfcgTUY2b8POTBUw9AwmTaehvSVnEAbcgJHsPheFMP8WKDFDk2I+aNTLRQX5iP21dPwz3CGLVA==
|
||||
"@types/bull@3.15.5":
|
||||
version "3.15.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/bull/-/bull-3.15.5.tgz#a4459c127c5b10fb847531579a2cd5db35751366"
|
||||
integrity sha512-XgJQWJ03jyKMfdoL8IAIoHIo7JkkL74kcxuujTONkSJswm0giIJ9kuVgDNHS0OvD+OiPNcFmbBl0H3scj2+A8A==
|
||||
dependencies:
|
||||
"@types/ioredis" "*"
|
||||
|
||||
|
@ -734,10 +734,10 @@
|
|||
dependencies:
|
||||
"@types/koa" "*"
|
||||
|
||||
"@types/koa-logger@3.1.1":
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/koa-logger/-/koa-logger-3.1.1.tgz#bf759ba2f25b7562ffa30b09616bb1c1e5070fea"
|
||||
integrity sha512-wp2HaskkPugfwgXgNnc+idnReuJZSTTYQbkcxXjsMhp1kTc342PxDzTL9FXDgBfEvgt9NX1CCGjkwPKX2dlEKQ==
|
||||
"@types/koa-logger@3.1.2":
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/koa-logger/-/koa-logger-3.1.2.tgz#91e890f405ddb0626bc385767e4cc0cd7226d1a8"
|
||||
integrity sha512-sioTA1xlKYiIgryANWPRHBkG3XGbWftw9slWADUPC+qvPIY/yRLSrhvX7zkJwMrntub5dPO0GuAoyGGf0yitfQ==
|
||||
dependencies:
|
||||
"@types/koa" "*"
|
||||
|
||||
|
@ -816,14 +816,13 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-2.1.0.tgz#ea3dd64c4805597311790b61e872cbd1ed2cd806"
|
||||
integrity sha512-Q7DYAOi9O/+cLLhdaSvKdaumWyHbm7HAk/bFwwyTuU0arR5yyCeW5GOoqt4tJTpDRxhpx9Q8kQL6vMpuw9hDSw==
|
||||
|
||||
"@types/markdown-it@12.2.1":
|
||||
version "12.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-12.2.1.tgz#ca36e1edce6f15a770f3e99e68622d1d2e2f0c65"
|
||||
integrity sha512-iij+ilRX/vxtUPCREjn74xzHo/RorHJDwOsJ6X+TgKw7zSvazhVXnDfwlTnyLOMdiVUjtRYU4CrcUZ7Aci4PmQ==
|
||||
"@types/markdown-it@12.2.3":
|
||||
version "12.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-12.2.3.tgz#0d6f6e5e413f8daaa26522904597be3d6cd93b51"
|
||||
integrity sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==
|
||||
dependencies:
|
||||
"@types/linkify-it" "*"
|
||||
"@types/mdurl" "*"
|
||||
highlight.js "^10.7.2"
|
||||
|
||||
"@types/matter-js@0.17.5":
|
||||
version "0.17.5"
|
||||
|
@ -863,10 +862,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.6.2.tgz#331b7b9f8621c638284787c5559423822fdffc50"
|
||||
integrity sha512-LSw8TZt12ZudbpHc6EkIyDM3nHVWKYrAvGy6EAJfNfjusbwnThqjqxUKKRwuV3iWYeW/LYMzNgaq3MaLffQ2xA==
|
||||
|
||||
"@types/node@16.9.6":
|
||||
version "16.9.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.6.tgz#040a64d7faf9e5d9e940357125f0963012e66f04"
|
||||
integrity sha512-YHUZhBOMTM3mjFkXVcK+WwAcYmyhe1wL4lfqNtzI0b3qAy7yuSetnM7QJazgE5PFmgVTNGiLOgRFfJMqW7XpSQ==
|
||||
"@types/node@16.10.3":
|
||||
version "16.10.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.3.tgz#7a8f2838603ea314d1d22bb3171d899e15c57bd5"
|
||||
integrity sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==
|
||||
|
||||
"@types/node@^14.11.8", "@types/node@^14.14.31", "@types/node@^14.14.41":
|
||||
version "14.17.9"
|
||||
|
@ -880,11 +879,6 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/normalize-package-data@^2.4.0":
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
|
||||
integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==
|
||||
|
||||
"@types/nprogress@0.2.0":
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/nprogress/-/nprogress-0.2.0.tgz#86c593682d4199212a0509cc3c4d562bbbd6e45f"
|
||||
|
@ -1176,10 +1170,10 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/ws@7.4.7":
|
||||
version "7.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702"
|
||||
integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==
|
||||
"@types/ws@8.2.0":
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.2.0.tgz#75faefbe2328f3b833cb8dc640658328990d04f3"
|
||||
integrity sha512-cyeefcUCgJlEk+hk2h3N+MqKKsPViQgF5boi9TTHSK+PoR9KWBb/C5ccPcDyAqgsbAYHTwulch725DV84+pSpg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
|
@ -1195,48 +1189,48 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.2.tgz#808c9fa7e4517274ed555fa158f2de4b4f468e71"
|
||||
integrity sha512-HrCIVMLjE1MOozVoD86622S7aunluLb2PJdPfb3nYiEtohm8mIB/vyv0Fd37AdeMFrTUQXEunw78YloMA3Qilg==
|
||||
|
||||
"@typescript-eslint/parser@4.31.2":
|
||||
version "4.31.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.31.2.tgz#54aa75986e3302d91eff2bbbaa6ecfa8084e9c34"
|
||||
integrity sha512-EcdO0E7M/sv23S/rLvenHkb58l3XhuSZzKf6DBvLgHqOYdL6YFMYVtreGFWirxaU2mS1GYDby3Lyxco7X5+Vjw==
|
||||
"@typescript-eslint/parser@4.33.0":
|
||||
version "4.33.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899"
|
||||
integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "4.31.2"
|
||||
"@typescript-eslint/types" "4.31.2"
|
||||
"@typescript-eslint/typescript-estree" "4.31.2"
|
||||
"@typescript-eslint/scope-manager" "4.33.0"
|
||||
"@typescript-eslint/types" "4.33.0"
|
||||
"@typescript-eslint/typescript-estree" "4.33.0"
|
||||
debug "^4.3.1"
|
||||
|
||||
"@typescript-eslint/scope-manager@4.31.2":
|
||||
version "4.31.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.31.2.tgz#1d528cb3ed3bcd88019c20a57c18b897b073923a"
|
||||
integrity sha512-2JGwudpFoR/3Czq6mPpE8zBPYdHWFGL6lUNIGolbKQeSNv4EAiHaR5GVDQaLA0FwgcdcMtRk+SBJbFGL7+La5w==
|
||||
"@typescript-eslint/scope-manager@4.33.0":
|
||||
version "4.33.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3"
|
||||
integrity sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.31.2"
|
||||
"@typescript-eslint/visitor-keys" "4.31.2"
|
||||
"@typescript-eslint/types" "4.33.0"
|
||||
"@typescript-eslint/visitor-keys" "4.33.0"
|
||||
|
||||
"@typescript-eslint/types@4.31.2":
|
||||
version "4.31.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.31.2.tgz#2aea7177d6d744521a168ed4668eddbd912dfadf"
|
||||
integrity sha512-kWiTTBCTKEdBGrZKwFvOlGNcAsKGJSBc8xLvSjSppFO88AqGxGNYtF36EuEYG6XZ9vT0xX8RNiHbQUKglbSi1w==
|
||||
"@typescript-eslint/types@4.33.0":
|
||||
version "4.33.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72"
|
||||
integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==
|
||||
|
||||
"@typescript-eslint/typescript-estree@4.31.2":
|
||||
version "4.31.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.31.2.tgz#abfd50594d8056b37e7428df3b2d185ef2d0060c"
|
||||
integrity sha512-ieBq8U9at6PvaC7/Z6oe8D3czeW5d//Fo1xkF/s9394VR0bg/UaMYPdARiWyKX+lLEjY3w/FNZJxitMsiWv+wA==
|
||||
"@typescript-eslint/typescript-estree@4.33.0":
|
||||
version "4.33.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609"
|
||||
integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.31.2"
|
||||
"@typescript-eslint/visitor-keys" "4.31.2"
|
||||
"@typescript-eslint/types" "4.33.0"
|
||||
"@typescript-eslint/visitor-keys" "4.33.0"
|
||||
debug "^4.3.1"
|
||||
globby "^11.0.3"
|
||||
is-glob "^4.0.1"
|
||||
semver "^7.3.5"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@4.31.2":
|
||||
version "4.31.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.31.2.tgz#7d5b4a4705db7fe59ecffb273c1d082760f635cc"
|
||||
integrity sha512-PrBId7EQq2Nibns7dd/ch6S6/M4/iwLM9McbgeEbCXfxdwRUNxJ4UNreJ6Gh3fI2GNKNrWnQxKL7oCPmngKBug==
|
||||
"@typescript-eslint/visitor-keys@4.33.0":
|
||||
version "4.33.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd"
|
||||
integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.31.2"
|
||||
"@typescript-eslint/types" "4.33.0"
|
||||
eslint-visitor-keys "^2.0.0"
|
||||
|
||||
"@ungap/promise-all-settled@1.1.2":
|
||||
|
@ -1576,22 +1570,22 @@
|
|||
"@webassemblyjs/ast" "1.11.1"
|
||||
"@xtuc/long" "4.2.2"
|
||||
|
||||
"@webpack-cli/configtest@^1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.0.4.tgz#f03ce6311c0883a83d04569e2c03c6238316d2aa"
|
||||
integrity sha512-cs3XLy+UcxiP6bj0A6u7MLLuwdXJ1c3Dtc0RkKg+wiI1g/Ti1om8+/2hc2A2B60NbBNAbMgyBMHvyymWm/j4wQ==
|
||||
"@webpack-cli/configtest@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.1.0.tgz#8342bef0badfb7dfd3b576f2574ab80c725be043"
|
||||
integrity sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==
|
||||
|
||||
"@webpack-cli/info@^1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.3.0.tgz#9d78a31101a960997a4acd41ffd9b9300627fe2b"
|
||||
integrity sha512-ASiVB3t9LOKHs5DyVUcxpraBXDOKubYu/ihHhU+t1UPpxsivg6Od2E2qU4gJCekfEddzRBzHhzA/Acyw/mlK/w==
|
||||
"@webpack-cli/info@^1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.4.0.tgz#b9179c3227ab09cbbb149aa733475fcf99430223"
|
||||
integrity sha512-F6b+Man0rwE4n0409FyAJHStYA5OIZERxmnUfLVwv0mc0V1wLad3V7jqRlMkgKBeAq07jUvglacNaa6g9lOpuw==
|
||||
dependencies:
|
||||
envinfo "^7.7.3"
|
||||
|
||||
"@webpack-cli/serve@^1.5.2":
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.5.2.tgz#ea584b637ff63c5a477f6f21604b5a205b72c9ec"
|
||||
integrity sha512-vgJ5OLWadI8aKjDlOH3rb+dYyPd2GTZuQC/Tihjct6F9GpXGZINo3Y/IVuZVTM1eDQB+/AOsjPUWH/WySDaXvw==
|
||||
"@webpack-cli/serve@^1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.6.0.tgz#2c275aa05c895eccebbfc34cfb223c6e8bd591a2"
|
||||
integrity sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==
|
||||
|
||||
"@xtuc/ieee754@^1.2.0":
|
||||
version "1.2.0"
|
||||
|
@ -2094,10 +2088,10 @@ autwh@0.1.0:
|
|||
dependencies:
|
||||
oauth "0.9.15"
|
||||
|
||||
aws-sdk@2.992.0:
|
||||
version "2.992.0"
|
||||
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.992.0.tgz#3d8f663727cf3ea62a794a7499ae001fe6256abb"
|
||||
integrity sha512-FP/AOu1nxfaPJ6to05eHriBUzvPiNapEwy96sm5GNOL8/T38k9//H6UhxLJ/46CzxFMH/Mo/WFp0qwpS39ev5A==
|
||||
aws-sdk@2.1003.0:
|
||||
version "2.1003.0"
|
||||
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1003.0.tgz#f4ca218f466c524a90370b5604a3ad4cda4c0e08"
|
||||
integrity sha512-UEZveI1m7+/YsomU2tVxLMmlo5g3sr3ue+QMJ2UwbrvHZ+O9hr9vVia1lD+L8fYTQenOff95NFc02h3pDE3iDA==
|
||||
dependencies:
|
||||
buffer "4.9.2"
|
||||
events "1.1.1"
|
||||
|
@ -2503,10 +2497,10 @@ cache-content-type@^1.0.0:
|
|||
mime-types "^2.1.18"
|
||||
ylru "^1.2.0"
|
||||
|
||||
cacheable-lookup@6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-6.0.1.tgz#f32ab50c3212302d9f49aa094c8a7593c162af7c"
|
||||
integrity sha512-vaccXt7hUfa5UzrtbdzHTWnL6V6ir39QtLuvGZys32j4HboAeiWVhrcdAm8ecTz1rLubxPhec2n22BBb5/dgVA==
|
||||
cacheable-lookup@6.0.2:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-6.0.2.tgz#8df03d6239c91bb9f6394700d7ba4a100abbad67"
|
||||
integrity sha512-9RJkUl1k/A1dFhaRfrEUdISvvou0WKx8LboMO0j1BpsqgAuolwZgwaEtn0dmFMk5HQxpFtHF1bHCnIQMywUpvw==
|
||||
|
||||
cacheable-lookup@^5.0.3:
|
||||
version "5.0.3"
|
||||
|
@ -2619,12 +2613,12 @@ cbor@*:
|
|||
"@cto.af/textdecoder" "^0.0.0"
|
||||
nofilter "^2.0.3"
|
||||
|
||||
cbor@8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.0.0.tgz#51657d26a99a6a1866f8c3258e948576eb17d709"
|
||||
integrity sha512-nMmaLWbj7+bC6MsApKRIig8h+yjgNLhPLXaCelq5+C7mpWsHgIcseZSdvgexSY5uE1Q3m2uPvIDZwSdxdo7qig==
|
||||
cbor@8.0.2:
|
||||
version "8.0.2"
|
||||
resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.0.2.tgz#d0f5088423437efcc160e9304bd0576f45d06abb"
|
||||
integrity sha512-H5WTjQYgyHQI0VrCmbyQBOPy1353MjmUi/r3DbPib4U13vuyqm7es9Mfpe8G58bN/mCdRlJWkiCrPl1uM1wAlg==
|
||||
dependencies:
|
||||
nofilter "^3.0.2"
|
||||
nofilter "^3.0.3"
|
||||
|
||||
chalk@4.0.0:
|
||||
version "4.0.0"
|
||||
|
@ -3068,6 +3062,11 @@ colorette@^1.2.0, colorette@^1.2.1, colorette@^1.2.2:
|
|||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
|
||||
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
|
||||
|
||||
colorette@^2.0.14:
|
||||
version "2.0.15"
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.15.tgz#8e634aa0429b110d24be82eac4d42f5ea65ab2d5"
|
||||
integrity sha512-lIFQhufWaVvwi4wOlX9Gx5b0Nmw3XAZ8HzHNH9dfxhe+JaKNTmX6QLk4o7UHyI+tUY8ClvyfaHUm5bf61O3psA==
|
||||
|
||||
colormin@^1.0.5:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133"
|
||||
|
@ -3094,11 +3093,6 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
|
|||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
commander@8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-8.1.0.tgz#db36e3e66edf24ff591d639862c6ab2c52664362"
|
||||
integrity sha512-mf45ldcuHSYShkplHHGKWb4TrmwQadxOn7v4WuhDJy0ZVoY5JFajaRDKD0PNe5qXzBX0rhovjTnP6Kz9LETcuA==
|
||||
|
||||
commander@^2.12.1, commander@^2.19.0, commander@^2.20.0:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
|
@ -3165,15 +3159,14 @@ concat-stream@^1.5.2, concat-stream@^1.6.0:
|
|||
readable-stream "^2.2.2"
|
||||
typedarray "^0.0.6"
|
||||
|
||||
concurrently@6.2.1:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-6.2.1.tgz#d880fc1d77559084732fa514092a3d5109a0d5bf"
|
||||
integrity sha512-emgwhH+ezkuYKSHZQ+AkgEpoUZZlbpPVYCVv7YZx0r+T7fny1H03r2nYRebpi2DudHR4n1Rgbo2YTxKOxVJ4+g==
|
||||
concurrently@6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-6.3.0.tgz#63128cb4a6ed54d3c0ed8528728590a5fe54582a"
|
||||
integrity sha512-k4k1jQGHHKsfbqzkUszVf29qECBrkvBKkcPJEUDTyVR7tZd1G/JOfnst4g1sYbFvJ4UjHZisj1aWQR8yLKpGPw==
|
||||
dependencies:
|
||||
chalk "^4.1.0"
|
||||
date-fns "^2.16.1"
|
||||
lodash "^4.17.21"
|
||||
read-pkg "^5.2.0"
|
||||
rxjs "^6.6.3"
|
||||
spawn-command "^0.0.2-1"
|
||||
supports-color "^8.1.0"
|
||||
|
@ -3558,10 +3551,10 @@ csstype@^2.6.8:
|
|||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.13.tgz#a6893015b90e84dd6e85d0e3b442a1e84f2dbe0f"
|
||||
integrity sha512-ul26pfSQTZW8dcOnD2iiJssfXw0gdNVX9IJDH/X3K5DGPfj+fUYe3kB+swUY6BF3oZDxaID3AJt+9/ojSAE05A==
|
||||
|
||||
cypress@8.4.1:
|
||||
version "8.4.1"
|
||||
resolved "https://registry.yarnpkg.com/cypress/-/cypress-8.4.1.tgz#8b5898bf49359cadc28f02ba05d51f63b8e3a717"
|
||||
integrity sha512-itJXq0Vx3sXCUrDyBi2IUrkxVu/gTTp1VhjB5tzGgkeCR8Ae+/T8WV63rsZ7fS8Tpq7LPPXiyoM/sEdOX7cR6A==
|
||||
cypress@8.5.0:
|
||||
version "8.5.0"
|
||||
resolved "https://registry.yarnpkg.com/cypress/-/cypress-8.5.0.tgz#5712ca170913f8344bf167301205c4217c1eb9bd"
|
||||
integrity sha512-MMkXIS+Ro2KETn4gAlG3tIc/7FiljuuCZP0zpd9QsRG6MZSyZW/l1J3D4iQM6WHsVxuX4rFChn5jPFlC2tNSvQ==
|
||||
dependencies:
|
||||
"@cypress/request" "^2.88.6"
|
||||
"@cypress/xvfb" "^1.2.4"
|
||||
|
@ -3597,6 +3590,7 @@ cypress@8.4.1:
|
|||
minimist "^1.2.5"
|
||||
ospath "^1.2.2"
|
||||
pretty-bytes "^5.6.0"
|
||||
proxy-from-env "1.0.0"
|
||||
ramda "~0.27.1"
|
||||
request-progress "^3.0.0"
|
||||
supports-color "^8.1.1"
|
||||
|
@ -4268,10 +4262,10 @@ escodegen@^2.0.0:
|
|||
optionalDependencies:
|
||||
source-map "~0.6.1"
|
||||
|
||||
eslint-plugin-vue@7.18.0:
|
||||
version "7.18.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-7.18.0.tgz#02a452142330c7f27c242db21a1b9e25238540f6"
|
||||
integrity sha512-ceDXlXYMMPMSXw7tdKUR42w9jlzthJGJ3Kvm3YrZ0zuQfvAySNxe8sm6VHuksBW0+060GzYXhHJG6IHVOfF83Q==
|
||||
eslint-plugin-vue@7.19.1:
|
||||
version "7.19.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-7.19.1.tgz#435fb2ce712842a9530b28eacb883680e8eaa4f3"
|
||||
integrity sha512-e2pD7nW2sTY04ThH+66BgToNwC4n6dqfNhKE+ypdJFtZgn3Zn+nP8ZEIFPG0PGqCKQ3qxy8dJk1bzUsuQd3ANA==
|
||||
dependencies:
|
||||
eslint-utils "^2.1.0"
|
||||
natural-compare "^1.4.0"
|
||||
|
@ -5101,10 +5095,10 @@ glob@7.1.6, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
|
|||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@7.1.7:
|
||||
version "7.1.7"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
|
||||
integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
|
||||
glob@7.2.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
|
||||
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
|
@ -5261,13 +5255,13 @@ gulp-replace@1.1.3:
|
|||
replacestream "^4.0.3"
|
||||
yargs-parser ">=5.0.0-security.0"
|
||||
|
||||
gulp-terser@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/gulp-terser/-/gulp-terser-2.0.1.tgz#5f8f4fc54588b79519243809cc8eef4936286d0d"
|
||||
integrity sha512-XCrnCXP8ovNpgLK9McJIXlgm0j3W2TsiWu7K9y3m+Sn5XZgUzi6U8MPHtS3NdLMic9poCj695N0ARJ2B6atypw==
|
||||
gulp-terser@2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/gulp-terser/-/gulp-terser-2.1.0.tgz#149b693a1adbde922807b60b844bb7351dafbde1"
|
||||
integrity sha512-lQ3+JUdHDVISAlUIUSZ/G9Dz/rBQHxOiYDQ70IVWFQeh4b33TC1MCIU+K18w07PS3rq/CVc34aQO4SUbdaNMPQ==
|
||||
dependencies:
|
||||
plugin-error "^1.0.1"
|
||||
terser "5.4.0"
|
||||
terser "^5.9.0"
|
||||
through2 "^4.0.2"
|
||||
vinyl-sourcemaps-apply "^0.2.1"
|
||||
|
||||
|
@ -5408,11 +5402,6 @@ highlight.js@^10.7.1:
|
|||
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.2.tgz#89319b861edc66c48854ed1e6da21ea89f847360"
|
||||
integrity sha512-oFLl873u4usRM9K63j4ME9u3etNF0PLiJhSQ8rdfuL51Wn3zkD6drf9ZW0dOzjnZI22YYG24z30JcmfCZjMgYg==
|
||||
|
||||
highlight.js@^10.7.2:
|
||||
version "10.7.3"
|
||||
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
|
||||
integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==
|
||||
|
||||
homedir-polyfill@^1.0.1:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8"
|
||||
|
@ -6535,6 +6524,14 @@ koa-convert@^1.2.0:
|
|||
co "^4.6.0"
|
||||
koa-compose "^3.0.0"
|
||||
|
||||
koa-convert@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-2.0.0.tgz#86a0c44d81d40551bae22fee6709904573eea4f5"
|
||||
integrity sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==
|
||||
dependencies:
|
||||
co "^4.6.0"
|
||||
koa-compose "^4.1.0"
|
||||
|
||||
koa-favicon@2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/koa-favicon/-/koa-favicon-2.1.0.tgz#c430cc594614fb494adcb5ee1196a2f7f53ea442"
|
||||
|
@ -6627,6 +6624,35 @@ koa@2.13.1:
|
|||
type-is "^1.6.16"
|
||||
vary "^1.1.2"
|
||||
|
||||
koa@2.13.3:
|
||||
version "2.13.3"
|
||||
resolved "https://registry.yarnpkg.com/koa/-/koa-2.13.3.tgz#a62641ba753ec54bee2c6da1a4f294c5fac35407"
|
||||
integrity sha512-XhXIoR+ylAwqG3HhXwnMPQAM/4xfywz52OvxZNmxmTWGGHsvmBv4NSIhURha6yMuvEex1WdtplUTHnxnKpQiGw==
|
||||
dependencies:
|
||||
accepts "^1.3.5"
|
||||
cache-content-type "^1.0.0"
|
||||
content-disposition "~0.5.2"
|
||||
content-type "^1.0.4"
|
||||
cookies "~0.8.0"
|
||||
debug "^4.3.2"
|
||||
delegates "^1.0.0"
|
||||
depd "^2.0.0"
|
||||
destroy "^1.0.4"
|
||||
encodeurl "^1.0.2"
|
||||
escape-html "^1.0.3"
|
||||
fresh "~0.5.2"
|
||||
http-assert "^1.3.0"
|
||||
http-errors "^1.6.3"
|
||||
is-generator-function "^1.0.7"
|
||||
koa-compose "^4.1.0"
|
||||
koa-convert "^2.0.0"
|
||||
on-finished "^2.3.0"
|
||||
only "~0.0.2"
|
||||
parseurl "^1.3.2"
|
||||
statuses "^1.5.0"
|
||||
type-is "^1.6.16"
|
||||
vary "^1.1.2"
|
||||
|
||||
ky-universal@^0.8.2:
|
||||
version "0.8.2"
|
||||
resolved "https://registry.yarnpkg.com/ky-universal/-/ky-universal-0.8.2.tgz#edc398d54cf495d7d6830aa1ab69559a3cc7f824"
|
||||
|
@ -7116,10 +7142,10 @@ methods@^1.1.2:
|
|||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
|
||||
|
||||
mfm-js@0.19.0:
|
||||
version "0.19.0"
|
||||
resolved "https://registry.yarnpkg.com/mfm-js/-/mfm-js-0.19.0.tgz#77419fb54cb1bcfa49d931467903d6dc6c1815e1"
|
||||
integrity sha512-06uVz9v+ltO9Obt1vWpwfJZEyZloihYuoxud+Rt6R9PW4aUk2/zua8qUeMBDNQkKZ/+IRtRSmmgn2A2GiDSMEg==
|
||||
mfm-js@0.20.0:
|
||||
version "0.20.0"
|
||||
resolved "https://registry.yarnpkg.com/mfm-js/-/mfm-js-0.20.0.tgz#3afdcd7959461fd825aa8af9b9e8a57cdbddc290"
|
||||
integrity sha512-1+3tV3nWUKQNh/ztX3wXu5iLBtdsg6q3wUhl+XyOhc2H3sQdG+sih/w2c0nR9TIawjN+Z1/pvgGzxMJHfmKQmA==
|
||||
dependencies:
|
||||
twemoji-parser "13.1.x"
|
||||
|
||||
|
@ -7402,11 +7428,6 @@ nano-time@1.0.0:
|
|||
dependencies:
|
||||
big-integer "^1.6.16"
|
||||
|
||||
nanocolors@^0.1.5:
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/nanocolors/-/nanocolors-0.1.6.tgz#bc2350d3edfdbfadd7ac018c855ae7c13905a6ad"
|
||||
integrity sha512-2pvTw6vYRaBLGir2xR7MxaJtyWkrn+C53EpW8yPotG+pdAwBvt0Xwk4VJ6VHLY0aLthVZPvDfm9TdZvrvAm5UQ==
|
||||
|
||||
nanoid@3.1.20, nanoid@^3.1.20:
|
||||
version "3.1.20"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788"
|
||||
|
@ -7417,10 +7438,10 @@ nanoid@^3.1.23:
|
|||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81"
|
||||
integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==
|
||||
|
||||
nanoid@^3.1.25:
|
||||
version "3.1.25"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152"
|
||||
integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==
|
||||
nanoid@^3.1.28:
|
||||
version "3.1.29"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.29.tgz#214fb2d7a33e1a5bef4757b779dfaeb6a4e5aeb4"
|
||||
integrity sha512-dW2pUSGZ8ZnCFIlBIA31SV8huOGCHb6OwzVCc7A69rb/a+SgPBwfmLvK5TKQ3INPbRkcI8a/Owo0XbiTNH19wg==
|
||||
|
||||
nanomatch@^1.2.9:
|
||||
version "1.2.13"
|
||||
|
@ -7539,10 +7560,10 @@ node-releases@^1.1.70, node-releases@^1.1.71:
|
|||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb"
|
||||
integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==
|
||||
|
||||
nodemailer@6.6.3:
|
||||
version "6.6.3"
|
||||
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.6.3.tgz#31fb53dd4d8ae16fc088a65cb9ffa8d928a69b48"
|
||||
integrity sha512-faZFufgTMrphYoDjvyVpbpJcYzwyFnbAMmQtj1lVBYAUSm3SOy2fIdd9+Mr4UxPosBa0JRw9bJoIwQn+nswiew==
|
||||
nodemailer@6.6.5:
|
||||
version "6.6.5"
|
||||
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.6.5.tgz#f9f6953cee5cfe82cbea152eeddacf7a0442049a"
|
||||
integrity sha512-C/v856DBijUzHcHIgGpQoTrfsH3suKIRAGliIzCstatM2cAa+MYX3LuyCrABiO/cdJTxgBBHXxV1ztiqUwst5A==
|
||||
|
||||
nofilter@^2.0.3:
|
||||
version "2.0.3"
|
||||
|
@ -7551,7 +7572,7 @@ nofilter@^2.0.3:
|
|||
dependencies:
|
||||
"@cto.af/textdecoder" "^0.0.0"
|
||||
|
||||
nofilter@^3.0.2:
|
||||
nofilter@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.0.3.tgz#3ff3b142efdccb403434ccae4a0c2c835cb9b522"
|
||||
integrity sha512-TN/MCrQmXQk5DyUJ8TGUq1Il8rv4fTsjddLmMopV006QP8DMkglmGgYfQKD5620vXLRXfr8iGI6ZZ4/ZWld2cQ==
|
||||
|
@ -7571,7 +7592,7 @@ nopt@^5.0.0:
|
|||
dependencies:
|
||||
abbrev "1"
|
||||
|
||||
normalize-package-data@^2.3.2, normalize-package-data@^2.5.0:
|
||||
normalize-package-data@^2.3.2:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
|
||||
integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
|
||||
|
@ -8196,6 +8217,11 @@ pgpass@1.x:
|
|||
dependencies:
|
||||
split "^1.0.0"
|
||||
|
||||
picocolors@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f"
|
||||
integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.0.7, picomatch@^2.2.1:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
||||
|
@ -8755,13 +8781,13 @@ postcss-zindex@^2.0.1:
|
|||
postcss "^5.0.4"
|
||||
uniqs "^2.0.0"
|
||||
|
||||
postcss@8.3.7:
|
||||
version "8.3.7"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.7.tgz#ec88563588c8da8e58e7226f7633b51ae221eeda"
|
||||
integrity sha512-9SaY7nnyQ63/WittqZYAvkkYPyKxchMKH71UDzeTmWuLSvxTRpeEeABZAzlCi55cuGcoFyoV/amX2BdsafQidQ==
|
||||
postcss@8.3.9:
|
||||
version "8.3.9"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.9.tgz#98754caa06c4ee9eb59cc48bd073bb6bd3437c31"
|
||||
integrity sha512-f/ZFyAKh9Dnqytx5X62jgjhhzttjZS7hMsohcI7HEI5tjELX/HxCy3EFhsRxyzGvrzFF+82XPvCS8T9TFleVJw==
|
||||
dependencies:
|
||||
nanocolors "^0.1.5"
|
||||
nanoid "^3.1.25"
|
||||
nanoid "^3.1.28"
|
||||
picocolors "^0.2.1"
|
||||
source-map-js "^0.6.2"
|
||||
|
||||
postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.8, postcss@^5.2.16:
|
||||
|
@ -8951,6 +8977,11 @@ proto-list@~1.2.1:
|
|||
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
|
||||
integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=
|
||||
|
||||
proxy-from-env@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
|
||||
integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=
|
||||
|
||||
ps-tree@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd"
|
||||
|
@ -9248,16 +9279,6 @@ read-pkg@^1.0.0:
|
|||
normalize-package-data "^2.3.2"
|
||||
path-type "^1.0.0"
|
||||
|
||||
read-pkg@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc"
|
||||
integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==
|
||||
dependencies:
|
||||
"@types/normalize-package-data" "^2.4.0"
|
||||
normalize-package-data "^2.5.0"
|
||||
parse-json "^5.0.0"
|
||||
type-fest "^0.6.0"
|
||||
|
||||
readable-stream@1.1.x:
|
||||
version "1.1.14"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
|
||||
|
@ -10046,6 +10067,14 @@ source-map-support@~0.5.19:
|
|||
buffer-from "^1.0.0"
|
||||
source-map "^0.6.0"
|
||||
|
||||
source-map-support@~0.5.20:
|
||||
version "0.5.20"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9"
|
||||
integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==
|
||||
dependencies:
|
||||
buffer-from "^1.0.0"
|
||||
source-map "^0.6.0"
|
||||
|
||||
source-map-url@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
|
||||
|
@ -10591,10 +10620,10 @@ syslog-pro@1.0.0:
|
|||
dependencies:
|
||||
moment "^2.22.2"
|
||||
|
||||
systeminformation@5.9.3:
|
||||
version "5.9.3"
|
||||
resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.9.3.tgz#d247c3fc27dc51787af28f0b4a0d82f9bce7f88b"
|
||||
integrity sha512-FmifqCPDU5uJZeORt1jCiATBTHwpX7luDzeFo8lojYbEiJk6oR3mtAZBOayCo3iEmgSILzmbcO855OXPHCeU+g==
|
||||
systeminformation@5.9.4:
|
||||
version "5.9.4"
|
||||
resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.9.4.tgz#1f0e29e0aa376dec8f69cc517eeefc5cdcda411a"
|
||||
integrity sha512-FOsiTn0CyJZoj9kIhla11ndsMzbbwwuriul81wpqIBt9IpbxHZ6P/oZCphIFgJrwqjTnme0Qp1HDzIkUD9Xr/g==
|
||||
|
||||
syuilo-password-strength@0.0.1:
|
||||
version "0.0.1"
|
||||
|
@ -10708,15 +10737,6 @@ terser-webpack-plugin@^5.1.3:
|
|||
source-map "^0.6.1"
|
||||
terser "^5.7.0"
|
||||
|
||||
terser@5.4.0:
|
||||
version "5.4.0"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.4.0.tgz#9815c0839072d5c894e22c6fc508fbe9f5e7d7e8"
|
||||
integrity sha512-3dZunFLbCJis9TAF2VnX+VrQLctRUmt1p3W2kCsJuZE4ZgWqh//+1MZ62EanewrqKoUf4zIaDGZAvml4UDc0OQ==
|
||||
dependencies:
|
||||
commander "^2.20.0"
|
||||
source-map "~0.7.2"
|
||||
source-map-support "~0.5.19"
|
||||
|
||||
terser@^5.5.1:
|
||||
version "5.5.1"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.5.1.tgz#540caa25139d6f496fdea056e414284886fb2289"
|
||||
|
@ -10735,6 +10755,15 @@ terser@^5.7.0:
|
|||
source-map "~0.7.2"
|
||||
source-map-support "~0.5.19"
|
||||
|
||||
terser@^5.9.0:
|
||||
version "5.9.0"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.9.0.tgz#47d6e629a522963240f2b55fcaa3c99083d2c351"
|
||||
integrity sha512-h5hxa23sCdpzcye/7b8YqbE5OwKca/ni0RQz1uRX3tGh8haaGHqcuSqbGRybuAKNdntZ0mDgFNXPJ48xQ2RXKQ==
|
||||
dependencies:
|
||||
commander "^2.20.0"
|
||||
source-map "~0.7.2"
|
||||
source-map-support "~0.5.20"
|
||||
|
||||
text-table@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
||||
|
@ -10961,10 +10990,10 @@ ts-node@10.2.1:
|
|||
make-error "^1.1.1"
|
||||
yn "3.1.1"
|
||||
|
||||
tsc-alias@1.3.9:
|
||||
version "1.3.9"
|
||||
resolved "https://registry.yarnpkg.com/tsc-alias/-/tsc-alias-1.3.9.tgz#0c9d1dd571c0a97af8159d20e7cd4ce6aaab1799"
|
||||
integrity sha512-PXNsdsuygWpvQrt41D7CBndJyZ+8Juf2BDvQ1OJNqq0QsMR4i+A4rfniY/NVwb70gW4nMDJBvNxxvzLJjakXtQ==
|
||||
tsc-alias@1.3.10:
|
||||
version "1.3.10"
|
||||
resolved "https://registry.yarnpkg.com/tsc-alias/-/tsc-alias-1.3.10.tgz#6ccf81c644092387ab9be3a3a75549a95eeffd80"
|
||||
integrity sha512-7SF56qiV7Oh/bON+XjF/uAzEFqbmwCuEIHQyoTyVJAK80WnxaIyhO9TBwD/x8InIMU8lnvExQBOrgKkRPsHH+w==
|
||||
dependencies:
|
||||
"@jfonx/console-utils" "^1.0.3"
|
||||
"@jfonx/file-utils" "^3.0.1"
|
||||
|
@ -11100,11 +11129,6 @@ type-fest@^0.21.3:
|
|||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
|
||||
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
|
||||
|
||||
type-fest@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"
|
||||
integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==
|
||||
|
||||
type-is@^1.6.14, type-is@^1.6.16, type-is@^1.6.4:
|
||||
version "1.6.18"
|
||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
|
||||
|
@ -11140,10 +11164,10 @@ typedarray@^0.0.6:
|
|||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||
|
||||
typeorm@0.2.37:
|
||||
version "0.2.37"
|
||||
resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.37.tgz#1a5e59216077640694d27c04c99ed3f968d15dc8"
|
||||
integrity sha512-7rkW0yCgFC24I5T0f3S/twmLSuccPh1SQmxET/oDWn2sSDVzbyWdnItSdKy27CdJGTlKHYtUVeOcMYw5LRsXVw==
|
||||
typeorm@0.2.38:
|
||||
version "0.2.38"
|
||||
resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.38.tgz#2af08079919f6ab04cd17017f9faa2c8d5cd566f"
|
||||
integrity sha512-M6Y3KQcAREQcphOVJciywf4mv6+A0I/SeR+lWNjKsjnQ+a3XcMwGYMGL0Jonsx3H0Cqlf/3yYqVki1jIXSK/xg==
|
||||
dependencies:
|
||||
"@sqltools/formatter" "^1.2.2"
|
||||
app-root-path "^3.0.0"
|
||||
|
@ -11615,16 +11639,16 @@ webidl-conversions@^6.1.0:
|
|||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514"
|
||||
integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
|
||||
|
||||
webpack-cli@4.8.0:
|
||||
version "4.8.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.8.0.tgz#5fc3c8b9401d3c8a43e2afceacfa8261962338d1"
|
||||
integrity sha512-+iBSWsX16uVna5aAYN6/wjhJy1q/GKk4KjKvfg90/6hykCTSgozbfz5iRgDTSJt/LgSbYxdBX3KBHeobIs+ZEw==
|
||||
webpack-cli@4.9.0:
|
||||
version "4.9.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.9.0.tgz#dc43e6e0f80dd52e89cbf73d5294bcd7ad6eb343"
|
||||
integrity sha512-n/jZZBMzVEl4PYIBs+auy2WI0WTQ74EnJDiyD98O2JZY6IVIHJNitkYp/uTXOviIOMfgzrNvC9foKv/8o8KSZw==
|
||||
dependencies:
|
||||
"@discoveryjs/json-ext" "^0.5.0"
|
||||
"@webpack-cli/configtest" "^1.0.4"
|
||||
"@webpack-cli/info" "^1.3.0"
|
||||
"@webpack-cli/serve" "^1.5.2"
|
||||
colorette "^1.2.1"
|
||||
"@webpack-cli/configtest" "^1.1.0"
|
||||
"@webpack-cli/info" "^1.4.0"
|
||||
"@webpack-cli/serve" "^1.6.0"
|
||||
colorette "^2.0.14"
|
||||
commander "^7.0.0"
|
||||
execa "^5.0.0"
|
||||
fastest-levenshtein "^1.0.12"
|
||||
|
@ -11655,10 +11679,10 @@ webpack-sources@^3.2.0:
|
|||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.0.tgz#b16973bcf844ebcdb3afde32eda1c04d0b90f89d"
|
||||
integrity sha512-fahN08Et7P9trej8xz/Z7eRu8ltyiygEo/hnRi9KqBUs80KeDcnf96ZJo++ewWd84fEf3xSX9bp4ZS9hbw0OBw==
|
||||
|
||||
webpack@5.54.0:
|
||||
version "5.54.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.54.0.tgz#629f0cd14c7a4340af758a3c7cef25c50670ae4d"
|
||||
integrity sha512-MAVKJMsIUotOQKzFOmN8ZkmMlj7BOyjDU6t1lomW9dWOme5WTStzGa3HMLdV1KYD1AiFETGsznL4LMSvj4tukw==
|
||||
webpack@5.58.0:
|
||||
version "5.58.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.58.0.tgz#9ec621cf8534f23c25e779e7c35dfde1211d5ccb"
|
||||
integrity sha512-xc2k5MLbR1iah24Z5xUm1nBh1PZXEdUnrX6YkTSOScq/VWbl5JCLREXJzGYqEAUbIO8tZI+Dzv82lGtnuUnVCQ==
|
||||
dependencies:
|
||||
"@types/eslint-scope" "^3.7.0"
|
||||
"@types/estree" "^0.0.50"
|
||||
|
@ -11857,10 +11881,10 @@ wrappy@1:
|
|||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
ws@8.2.2:
|
||||
version "8.2.2"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.2.tgz#ca684330c6dd6076a737250ed81ac1606cb0a63e"
|
||||
integrity sha512-Q6B6H2oc8QY3llc3cB8kVmQ6pnJWVQbP7Q5algTcIxx7YEpc0oU4NBVHlztA7Ekzfhw2r0rPducMUiCGWKQRzw==
|
||||
ws@8.2.3:
|
||||
version "8.2.3"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
|
||||
integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==
|
||||
|
||||
ws@^7.4.6:
|
||||
version "7.5.3"
|
||||
|
|
Loading…
Reference in a new issue