Merge branch 'develop' into abuse-report-resolver
This commit is contained in:
commit
3f6f9cea3e
|
@ -14,8 +14,13 @@
|
||||||
|
|
||||||
## 13.x.x (unreleased)
|
## 13.x.x (unreleased)
|
||||||
|
|
||||||
|
### General
|
||||||
|
- identicon生成を無効にしてパフォーマンスを向上させることができるようになりました
|
||||||
|
- サーバーのマシン情報の公開を無効にしてパフォーマンスを向上させることができるようになりました
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
- Fix: サーバーメトリクスが90度傾いている
|
- Fix: サーバーメトリクスが90度傾いている
|
||||||
|
- Fix: sparkle内にリンクを入れるとクリック不能になる問題の修正
|
||||||
|
|
||||||
### General
|
### General
|
||||||
- 通報の即時解決機能の追加
|
- 通報の即時解決機能の追加
|
||||||
|
|
|
@ -1066,6 +1066,8 @@ export interface Locale {
|
||||||
"additionalEmojiDictionary": string;
|
"additionalEmojiDictionary": string;
|
||||||
"installed": string;
|
"installed": string;
|
||||||
"branding": string;
|
"branding": string;
|
||||||
|
"enableServerMachineStats": string;
|
||||||
|
"enableIdenticonGeneration": string;
|
||||||
"_initialAccountSetting": {
|
"_initialAccountSetting": {
|
||||||
"accountCreated": string;
|
"accountCreated": string;
|
||||||
"letsStartAccountSetup": string;
|
"letsStartAccountSetup": string;
|
||||||
|
|
|
@ -1063,6 +1063,8 @@ goToMisskey: "Misskeyへ"
|
||||||
additionalEmojiDictionary: "絵文字の追加辞書"
|
additionalEmojiDictionary: "絵文字の追加辞書"
|
||||||
installed: "インストール済み"
|
installed: "インストール済み"
|
||||||
branding: "ブランディング"
|
branding: "ブランディング"
|
||||||
|
enableServerMachineStats: "サーバーのマシン情報を公開する"
|
||||||
|
enableIdenticonGeneration: "ユーザーごとのIdenticon生成を有効にする"
|
||||||
|
|
||||||
_initialAccountSetting:
|
_initialAccountSetting:
|
||||||
accountCreated: "アカウントの作成が完了しました!"
|
accountCreated: "アカウントの作成が完了しました!"
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
|
@ -0,0 +1,13 @@
|
||||||
|
export class AddMetaOptions1688280713783 {
|
||||||
|
name = 'AddMetaOptions1688280713783'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD "enableServerMachineStats" boolean NOT NULL DEFAULT false`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD "enableIdenticonGeneration" boolean NOT NULL DEFAULT true`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableIdenticonGeneration"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableServerMachineStats"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ import si from 'systeminformation';
|
||||||
import Xev from 'xev';
|
import Xev from 'xev';
|
||||||
import * as osUtils from 'os-utils';
|
import * as osUtils from 'os-utils';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import type { OnApplicationShutdown } from '@nestjs/common';
|
import type { OnApplicationShutdown } from '@nestjs/common';
|
||||||
|
|
||||||
const ev = new Xev();
|
const ev = new Xev();
|
||||||
|
@ -14,9 +15,10 @@ const round = (num: number) => Math.round(num * 10) / 10;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ServerStatsService implements OnApplicationShutdown {
|
export class ServerStatsService implements OnApplicationShutdown {
|
||||||
private intervalId: NodeJS.Timer;
|
private intervalId: NodeJS.Timer | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private metaService: MetaService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +26,9 @@ export class ServerStatsService implements OnApplicationShutdown {
|
||||||
* Report server stats regularly
|
* Report server stats regularly
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public start(): void {
|
public async start(): Promise<void> {
|
||||||
|
if (!(await this.metaService.fetch(true)).enableServerMachineStats) return;
|
||||||
|
|
||||||
const log = [] as any[];
|
const log = [] as any[];
|
||||||
|
|
||||||
ev.on('requestServerStatsLog', x => {
|
ev.on('requestServerStatsLog', x => {
|
||||||
|
@ -64,8 +68,10 @@ export class ServerStatsService implements OnApplicationShutdown {
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
|
if (this.intervalId) {
|
||||||
clearInterval(this.intervalId);
|
clearInterval(this.intervalId);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public onApplicationShutdown(signal?: string | undefined): void {
|
public onApplicationShutdown(signal?: string | undefined): void {
|
||||||
|
|
|
@ -413,6 +413,16 @@ export class Meta {
|
||||||
})
|
})
|
||||||
public enableChartsForFederatedInstances: boolean;
|
public enableChartsForFederatedInstances: boolean;
|
||||||
|
|
||||||
|
@Column('boolean', {
|
||||||
|
default: false,
|
||||||
|
})
|
||||||
|
public enableServerMachineStats: boolean;
|
||||||
|
|
||||||
|
@Column('boolean', {
|
||||||
|
default: true,
|
||||||
|
})
|
||||||
|
public enableIdenticonGeneration: boolean;
|
||||||
|
|
||||||
@Column('jsonb', {
|
@Column('jsonb', {
|
||||||
default: { },
|
default: { },
|
||||||
})
|
})
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { createTemp } from '@/misc/create-temp.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { LoggerService } from '@/core/LoggerService.js';
|
import { LoggerService } from '@/core/LoggerService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { ActivityPubServerService } from './ActivityPubServerService.js';
|
import { ActivityPubServerService } from './ActivityPubServerService.js';
|
||||||
import { NodeinfoServerService } from './NodeinfoServerService.js';
|
import { NodeinfoServerService } from './NodeinfoServerService.js';
|
||||||
import { ApiServerService } from './api/ApiServerService.js';
|
import { ApiServerService } from './api/ApiServerService.js';
|
||||||
|
@ -45,6 +46,7 @@ export class ServerService implements OnApplicationShutdown {
|
||||||
@Inject(DI.emojisRepository)
|
@Inject(DI.emojisRepository)
|
||||||
private emojisRepository: EmojisRepository,
|
private emojisRepository: EmojisRepository,
|
||||||
|
|
||||||
|
private metaService: MetaService,
|
||||||
private userEntityService: UserEntityService,
|
private userEntityService: UserEntityService,
|
||||||
private apiServerService: ApiServerService,
|
private apiServerService: ApiServerService,
|
||||||
private openApiServerService: OpenApiServerService,
|
private openApiServerService: OpenApiServerService,
|
||||||
|
@ -161,11 +163,16 @@ export class ServerService implements OnApplicationShutdown {
|
||||||
});
|
});
|
||||||
|
|
||||||
fastify.get<{ Params: { x: string } }>('/identicon/:x', async (request, reply) => {
|
fastify.get<{ Params: { x: string } }>('/identicon/:x', async (request, reply) => {
|
||||||
const [temp, cleanup] = await createTemp();
|
|
||||||
await genIdenticon(request.params.x, fs.createWriteStream(temp));
|
|
||||||
reply.header('Content-Type', 'image/png');
|
reply.header('Content-Type', 'image/png');
|
||||||
reply.header('Cache-Control', 'public, max-age=86400');
|
reply.header('Cache-Control', 'public, max-age=86400');
|
||||||
|
|
||||||
|
if ((await this.metaService.fetch()).enableIdenticonGeneration) {
|
||||||
|
const [temp, cleanup] = await createTemp();
|
||||||
|
await genIdenticon(request.params.x, fs.createWriteStream(temp));
|
||||||
return fs.createReadStream(temp).on('close', () => cleanup());
|
return fs.createReadStream(temp).on('close', () => cleanup());
|
||||||
|
} else {
|
||||||
|
return reply.redirect('/static-assets/avatar.png');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
fastify.get<{ Params: { code: string } }>('/verify-email/:code', async (request, reply) => {
|
fastify.get<{ Params: { code: string } }>('/verify-email/:code', async (request, reply) => {
|
||||||
|
|
|
@ -262,6 +262,14 @@ export const meta = {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
|
enableServerMachineStats: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
enableIdenticonGeneration: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
policies: {
|
policies: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
@ -364,6 +372,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
enableActiveEmailValidation: instance.enableActiveEmailValidation,
|
enableActiveEmailValidation: instance.enableActiveEmailValidation,
|
||||||
enableChartsForRemoteUser: instance.enableChartsForRemoteUser,
|
enableChartsForRemoteUser: instance.enableChartsForRemoteUser,
|
||||||
enableChartsForFederatedInstances: instance.enableChartsForFederatedInstances,
|
enableChartsForFederatedInstances: instance.enableChartsForFederatedInstances,
|
||||||
|
enableServerMachineStats: instance.enableServerMachineStats,
|
||||||
|
enableIdenticonGeneration: instance.enableIdenticonGeneration,
|
||||||
policies: { ...DEFAULT_POLICIES, ...instance.policies },
|
policies: { ...DEFAULT_POLICIES, ...instance.policies },
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -96,6 +96,8 @@ export const paramDef = {
|
||||||
enableActiveEmailValidation: { type: 'boolean' },
|
enableActiveEmailValidation: { type: 'boolean' },
|
||||||
enableChartsForRemoteUser: { type: 'boolean' },
|
enableChartsForRemoteUser: { type: 'boolean' },
|
||||||
enableChartsForFederatedInstances: { type: 'boolean' },
|
enableChartsForFederatedInstances: { type: 'boolean' },
|
||||||
|
enableServerMachineStats: { type: 'boolean' },
|
||||||
|
enableIdenticonGeneration: { type: 'boolean' },
|
||||||
serverRules: { type: 'array', items: { type: 'string' } },
|
serverRules: { type: 'array', items: { type: 'string' } },
|
||||||
preservedUsernames: { type: 'array', items: { type: 'string' } },
|
preservedUsernames: { type: 'array', items: { type: 'string' } },
|
||||||
},
|
},
|
||||||
|
@ -399,6 +401,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
set.enableChartsForFederatedInstances = ps.enableChartsForFederatedInstances;
|
set.enableChartsForFederatedInstances = ps.enableChartsForFederatedInstances;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ps.enableServerMachineStats !== undefined) {
|
||||||
|
set.enableServerMachineStats = ps.enableServerMachineStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ps.enableIdenticonGeneration !== undefined) {
|
||||||
|
set.enableIdenticonGeneration = ps.enableIdenticonGeneration;
|
||||||
|
}
|
||||||
|
|
||||||
if (ps.serverRules !== undefined) {
|
if (ps.serverRules !== undefined) {
|
||||||
set.serverRules = ps.serverRules;
|
set.serverRules = ps.serverRules;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ export const meta = {
|
||||||
tags: ['meta'],
|
tags: ['meta'],
|
||||||
|
|
||||||
requireCredential: false,
|
requireCredential: false,
|
||||||
|
allowGet: true,
|
||||||
|
cacheSec: 60 * 1,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -26,6 +26,8 @@ export const meta = {
|
||||||
tags: ['hashtags'],
|
tags: ['hashtags'],
|
||||||
|
|
||||||
requireCredential: false,
|
requireCredential: false,
|
||||||
|
allowGet: true,
|
||||||
|
cacheSec: 60 * 1,
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
|
|
|
@ -2,9 +2,12 @@ import * as os from 'node:os';
|
||||||
import si from 'systeminformation';
|
import si from 'systeminformation';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: false,
|
requireCredential: false,
|
||||||
|
allowGet: true,
|
||||||
|
cacheSec: 60 * 1,
|
||||||
|
|
||||||
tags: ['meta'],
|
tags: ['meta'],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -19,8 +22,24 @@ export const paramDef = {
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
constructor(
|
constructor(
|
||||||
|
private metaService: MetaService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async () => {
|
super(meta, paramDef, async () => {
|
||||||
|
if (!(await this.metaService.fetch()).enableServerMachineStats) return {
|
||||||
|
machine: '?',
|
||||||
|
cpu: {
|
||||||
|
model: '?',
|
||||||
|
cores: 0,
|
||||||
|
},
|
||||||
|
mem: {
|
||||||
|
total: 0,
|
||||||
|
},
|
||||||
|
fs: {
|
||||||
|
total: 0,
|
||||||
|
used: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const memStats = await si.mem();
|
const memStats = await si.mem();
|
||||||
const fsStats = await si.fsSize();
|
const fsStats = await si.fsSize();
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,13 @@ import TestWebGL2 from '@/workers/test-webgl2?worker';
|
||||||
import { WorkerMultiDispatch } from '@/scripts/worker-multi-dispatch';
|
import { WorkerMultiDispatch } from '@/scripts/worker-multi-dispatch';
|
||||||
import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash';
|
import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash';
|
||||||
|
|
||||||
const workerPromise = new Promise<WorkerMultiDispatch | null>(resolve => {
|
const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resolve => {
|
||||||
// テスト環境で Web Worker インスタンスは作成できない
|
// テスト環境で Web Worker インスタンスは作成できない
|
||||||
if (import.meta.env.MODE === 'test') {
|
if (import.meta.env.MODE === 'test') {
|
||||||
resolve(null);
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = 64;
|
||||||
|
canvas.height = 64;
|
||||||
|
resolve(canvas);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const testWorker = new TestWebGL2();
|
const testWorker = new TestWebGL2();
|
||||||
|
@ -38,7 +41,10 @@ const workerPromise = new Promise<WorkerMultiDispatch | null>(resolve => {
|
||||||
resolve(workers);
|
resolve(workers);
|
||||||
if (_DEV_) console.log('WebGL2 in worker is supported!');
|
if (_DEV_) console.log('WebGL2 in worker is supported!');
|
||||||
} else {
|
} else {
|
||||||
resolve(null);
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = 64;
|
||||||
|
canvas.height = 64;
|
||||||
|
resolve(canvas);
|
||||||
if (_DEV_) console.log('WebGL2 in worker is not supported...');
|
if (_DEV_) console.log('WebGL2 in worker is not supported...');
|
||||||
}
|
}
|
||||||
testWorker.terminate();
|
testWorker.terminate();
|
||||||
|
@ -70,6 +76,7 @@ const props = withDefaults(defineProps<{
|
||||||
width?: number;
|
width?: number;
|
||||||
cover?: boolean;
|
cover?: boolean;
|
||||||
forceBlurhash?: boolean;
|
forceBlurhash?: boolean;
|
||||||
|
onlyAvgColor?: boolean; // 軽量化のためにBlurhashを使わずに平均色だけを描画
|
||||||
}>(), {
|
}>(), {
|
||||||
transition: null,
|
transition: null,
|
||||||
src: null,
|
src: null,
|
||||||
|
@ -79,6 +86,7 @@ const props = withDefaults(defineProps<{
|
||||||
width: 64,
|
width: 64,
|
||||||
cover: true,
|
cover: true,
|
||||||
forceBlurhash: false,
|
forceBlurhash: false,
|
||||||
|
onlyAvgColor: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const viewId = uuid();
|
const viewId = uuid();
|
||||||
|
@ -139,8 +147,8 @@ function drawImage(bitmap: CanvasImageSource) {
|
||||||
ctx.drawImage(bitmap, 0, 0, canvasWidth, canvasHeight);
|
ctx.drawImage(bitmap, 0, 0, canvasWidth, canvasHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function draw() {
|
function drawAvg() {
|
||||||
if (!canvas.value || props.hash == null) return;
|
if (!canvas.value || !props.hash) return;
|
||||||
|
|
||||||
const ctx = canvas.value.getContext('2d');
|
const ctx = canvas.value.getContext('2d');
|
||||||
if (!ctx) return;
|
if (!ctx) return;
|
||||||
|
@ -149,25 +157,28 @@ async function draw() {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.fillStyle = extractAvgColorFromBlurhash(props.hash) ?? '#888';
|
ctx.fillStyle = extractAvgColorFromBlurhash(props.hash) ?? '#888';
|
||||||
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
||||||
|
}
|
||||||
|
|
||||||
const workers = await workerPromise;
|
async function draw() {
|
||||||
if (workers) {
|
if (props.hash == null) return;
|
||||||
workers.postMessage(
|
|
||||||
|
drawAvg();
|
||||||
|
|
||||||
|
if (props.onlyAvgColor) return;
|
||||||
|
|
||||||
|
const work = await canvasPromise;
|
||||||
|
if (work instanceof WorkerMultiDispatch) {
|
||||||
|
work.postMessage(
|
||||||
{
|
{
|
||||||
id: viewId,
|
id: viewId,
|
||||||
hash: props.hash,
|
hash: props.hash,
|
||||||
width: canvasWidth,
|
|
||||||
height: canvasHeight,
|
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
const work = document.createElement('canvas');
|
|
||||||
work.width = canvasWidth;
|
|
||||||
work.height = canvasHeight;
|
|
||||||
render(props.hash, work);
|
render(props.hash, work);
|
||||||
ctx.drawImage(work, 0, 0, canvasWidth, canvasHeight);
|
drawImage(work);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error occured during drawing blurhash', error);
|
console.error('Error occured during drawing blurhash', error);
|
||||||
}
|
}
|
||||||
|
@ -179,9 +190,9 @@ function workerOnMessage(event: MessageEvent) {
|
||||||
drawImage(event.data.bitmap as ImageBitmap);
|
drawImage(event.data.bitmap as ImageBitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
workerPromise.then(worker => {
|
canvasPromise.then(work => {
|
||||||
if (worker) {
|
if (work instanceof WorkerMultiDispatch) {
|
||||||
worker.addListener(workerOnMessage);
|
work.addListener(workerOnMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
draw();
|
draw();
|
||||||
|
@ -204,8 +215,10 @@ onMounted(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
workerPromise.then(worker => {
|
canvasPromise.then(work => {
|
||||||
worker?.removeListener(workerOnMessage);
|
if (work instanceof WorkerMultiDispatch) {
|
||||||
|
work.removeListener(workerOnMessage);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -32,7 +32,8 @@
|
||||||
</path>
|
</path>
|
||||||
</svg>
|
</svg>
|
||||||
-->
|
-->
|
||||||
<svg v-for="particle in particles" :key="particle.id" :width="width" :height="height" :viewBox="`0 0 ${width} ${height}`" xmlns="http://www.w3.org/2000/svg" style="position: absolute; top: -32px; left: -32px;">
|
<!-- MFMで上位レイヤーに表示されるため、リンクをクリックできるようにstyleにpointer-events: none;を付与。 -->
|
||||||
|
<svg v-for="particle in particles" :key="particle.id" :width="width" :height="height" :viewBox="`0 0 ${width} ${height}`" xmlns="http://www.w3.org/2000/svg" style="position: absolute; top: -32px; left: -32px; pointer-events: none;">
|
||||||
<path
|
<path
|
||||||
style="transform-origin: center; transform-box: fill-box;"
|
style="transform-origin: center; transform-box: fill-box;"
|
||||||
:transform="`translate(${particle.x} ${particle.y})`"
|
:transform="`translate(${particle.x} ${particle.y})`"
|
||||||
|
@ -115,6 +116,5 @@ onUnmounted(() => {
|
||||||
.root {
|
.root {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
pointer-events: none;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<component :is="link ? MkA : 'span'" v-user-preview="preview ? user.id : undefined" v-bind="bound" class="_noSelect" :class="[$style.root, { [$style.animation]: animation, [$style.cat]: user.isCat, [$style.square]: squareAvatars }]" :style="{ color }" :title="acct(user)" @click="onClick">
|
<component :is="link ? MkA : 'span'" v-user-preview="preview ? user.id : undefined" v-bind="bound" class="_noSelect" :class="[$style.root, { [$style.animation]: animation, [$style.cat]: user.isCat, [$style.square]: squareAvatars }]" :style="{ color }" :title="acct(user)" @click="onClick">
|
||||||
<img :class="$style.inner" :src="url" :hash="user?.avatarBlurhash" :cover="true"/>
|
<MkImgWithBlurhash :class="$style.inner" :src="url" :hash="user?.avatarBlurhash" :cover="true" :onlyAvgColor="true"/>
|
||||||
<MkUserOnlineIndicator v-if="indicator" :class="$style.indicator" :user="user"/>
|
<MkUserOnlineIndicator v-if="indicator" :class="$style.indicator" :user="user"/>
|
||||||
<div v-if="user.isCat" :class="[$style.ears]">
|
<div v-if="user.isCat" :class="[$style.ears]">
|
||||||
<div :class="$style.earLeft">
|
<div :class="$style.earLeft">
|
||||||
|
@ -24,6 +24,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { watch } from 'vue';
|
import { watch } from 'vue';
|
||||||
import * as misskey from 'misskey-js';
|
import * as misskey from 'misskey-js';
|
||||||
|
import MkImgWithBlurhash from '../MkImgWithBlurhash.vue';
|
||||||
import MkA from './MkA.vue';
|
import MkA from './MkA.vue';
|
||||||
import { getStaticImageUrl } from '@/scripts/media-proxy';
|
import { getStaticImageUrl } from '@/scripts/media-proxy';
|
||||||
import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash';
|
import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash';
|
||||||
|
|
|
@ -4,6 +4,14 @@
|
||||||
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
|
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
|
||||||
<FormSuspense :p="init">
|
<FormSuspense :p="init">
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
|
<MkSwitch v-model="enableServerMachineStats">
|
||||||
|
<template #label>{{ i18n.ts.enableServerMachineStats }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
|
||||||
|
<MkSwitch v-model="enableIdenticonGeneration">
|
||||||
|
<template #label>{{ i18n.ts.enableIdenticonGeneration }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
|
||||||
<MkSwitch v-model="enableChartsForRemoteUser">
|
<MkSwitch v-model="enableChartsForRemoteUser">
|
||||||
<template #label>{{ i18n.ts.enableChartsForRemoteUser }}</template>
|
<template #label>{{ i18n.ts.enableChartsForRemoteUser }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
|
@ -27,17 +35,23 @@ import { i18n } from '@/i18n';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
|
|
||||||
|
let enableServerMachineStats: boolean = $ref(false);
|
||||||
|
let enableIdenticonGeneration: boolean = $ref(false);
|
||||||
let enableChartsForRemoteUser: boolean = $ref(false);
|
let enableChartsForRemoteUser: boolean = $ref(false);
|
||||||
let enableChartsForFederatedInstances: boolean = $ref(false);
|
let enableChartsForFederatedInstances: boolean = $ref(false);
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await os.api('admin/meta');
|
const meta = await os.api('admin/meta');
|
||||||
|
enableServerMachineStats = meta.enableServerMachineStats;
|
||||||
|
enableIdenticonGeneration = meta.enableIdenticonGeneration;
|
||||||
enableChartsForRemoteUser = meta.enableChartsForRemoteUser;
|
enableChartsForRemoteUser = meta.enableChartsForRemoteUser;
|
||||||
enableChartsForFederatedInstances = meta.enableChartsForFederatedInstances;
|
enableChartsForFederatedInstances = meta.enableChartsForFederatedInstances;
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
os.apiWithDialog('admin/update-meta', {
|
os.apiWithDialog('admin/update-meta', {
|
||||||
|
enableServerMachineStats,
|
||||||
|
enableIdenticonGeneration,
|
||||||
enableChartsForRemoteUser,
|
enableChartsForRemoteUser,
|
||||||
enableChartsForFederatedInstances,
|
enableChartsForFederatedInstances,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
|
|
|
@ -73,7 +73,7 @@ let fetching = $ref(true);
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const [_stats, _onlineUsersCount] = await Promise.all([
|
const [_stats, _onlineUsersCount] = await Promise.all([
|
||||||
os.api('stats', {}),
|
os.api('stats', {}),
|
||||||
os.api('get-online-users-count').then(res => res.count),
|
os.apiGet('get-online-users-count').then(res => res.count),
|
||||||
]);
|
]);
|
||||||
stats = _stats;
|
stats = _stats;
|
||||||
onlineUsersCount = _onlineUsersCount;
|
onlineUsersCount = _onlineUsersCount;
|
||||||
|
|
|
@ -40,7 +40,7 @@ const { widgetProps, configure } = useWidgetPropsManager(name,
|
||||||
const onlineUsersCount = ref(0);
|
const onlineUsersCount = ref(0);
|
||||||
|
|
||||||
const tick = () => {
|
const tick = () => {
|
||||||
os.api('get-online-users-count').then(res => {
|
os.apiGet('get-online-users-count').then(res => {
|
||||||
onlineUsersCount.value = res.count;
|
onlineUsersCount.value = res.count;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -53,7 +53,7 @@ const stats = ref([]);
|
||||||
const fetching = ref(true);
|
const fetching = ref(true);
|
||||||
|
|
||||||
const fetch = () => {
|
const fetch = () => {
|
||||||
os.api('hashtags/trend').then(res => {
|
os.apiGet('hashtags/trend').then(res => {
|
||||||
stats.value = res;
|
stats.value = res;
|
||||||
fetching.value = false;
|
fetching.value = false;
|
||||||
});
|
});
|
||||||
|
|
|
@ -62,7 +62,7 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name,
|
||||||
|
|
||||||
const meta = ref(null);
|
const meta = ref(null);
|
||||||
|
|
||||||
os.api('server-info', {}).then(res => {
|
os.apiGet('server-info', {}).then(res => {
|
||||||
meta.value = res;
|
meta.value = res;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { render } from 'buraha';
|
import { render } from 'buraha';
|
||||||
|
|
||||||
|
const canvas = new OffscreenCanvas(64, 64);
|
||||||
|
|
||||||
onmessage = (event) => {
|
onmessage = (event) => {
|
||||||
// console.log(event.data);
|
// console.log(event.data);
|
||||||
if (!('id' in event.data && typeof event.data.id === 'string')) {
|
if (!('id' in event.data && typeof event.data.id === 'string')) {
|
||||||
|
@ -8,8 +10,8 @@ onmessage = (event) => {
|
||||||
if (!('hash' in event.data && typeof event.data.hash === 'string')) {
|
if (!('hash' in event.data && typeof event.data.hash === 'string')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const work = new OffscreenCanvas(event.data.width ?? 64, event.data.height ?? 64);
|
|
||||||
render(event.data.hash, work);
|
render(event.data.hash, canvas);
|
||||||
const bitmap = work.transferToImageBitmap();
|
const bitmap = canvas.transferToImageBitmap();
|
||||||
postMessage({ id: event.data.id, bitmap });
|
postMessage({ id: event.data.id, bitmap });
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue