Merge pull request #49 from sim1222/v12-renew

V12 package upgrade
This commit is contained in:
こけっち 2024-10-14 13:18:20 +09:00 committed by GitHub
commit 733fb0b6bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
249 changed files with 7592 additions and 6877 deletions

View File

@ -1,5 +1,4 @@
version: "3" version: "3"
services: services:
web: web:
build: . build: .
@ -7,7 +6,7 @@ services:
links: links:
- db - db
- redis - redis
# - es # - es
ports: ports:
- "3000:3000" - "3000:3000"
networks: networks:
@ -16,25 +15,22 @@ services:
volumes: volumes:
- ./files:/misskey/files - ./files:/misskey/files
- ./.config:/misskey/.config:ro - ./.config:/misskey/.config:ro
redis: redis:
restart: always restart: always
image: redis:4.0-alpine image: redis:alpine
networks: networks:
- internal_network - internal_network
volumes: volumes:
- ./redis:/data - ./redis:/data
db: db:
restart: always restart: always
image: postgres:12.2-alpine image: postgres:17.0-alpine
networks: networks:
- internal_network - internal_network
env_file: env_file:
- .config/docker.env - .config/docker.env
volumes: volumes:
- ./db:/var/lib/postgresql/data - ./db:/var/lib/postgresql/data
# es: # es:
# restart: always # restart: always
# image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.2 # image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.2
@ -45,7 +41,6 @@ services:
# - internal_network # - internal_network
# volumes: # volumes:
# - ./elasticsearch:/usr/share/elasticsearch/data # - ./elasticsearch:/usr/share/elasticsearch/data
networks: networks:
internal_network: internal_network:
internal: true internal: true

View File

@ -8,8 +8,8 @@
}, },
"private": true, "private": true,
"scripts": { "scripts": {
"postinstall": "node ./scripts/install-packages.js", "postinstall": "node ./scripts/install-packages.mjs",
"build": "node ./scripts/build.js", "build": "node ./scripts/build.mjs",
"start": "cd packages/backend && node --experimental-json-modules ./built/boot/index.js", "start": "cd packages/backend && node --experimental-json-modules ./built/boot/index.js",
"start:test": "cd packages/backend && cross-env NODE_ENV=test node --experimental-json-modules ./built/boot/index.js", "start:test": "cd packages/backend && cross-env NODE_ENV=test node --experimental-json-modules ./built/boot/index.js",
"init": "npm run migrate", "init": "npm run migrate",
@ -17,8 +17,8 @@
"migrateandstart": "npm run migrate && npm run start", "migrateandstart": "npm run migrate && npm run start",
"gulp": "gulp build", "gulp": "gulp build",
"watch": "npm run dev", "watch": "npm run dev",
"dev": "node ./scripts/dev.js", "dev": "node ./scripts/dev.mjs",
"lint": "node ./scripts/lint.js", "lint": "node ./scripts/lint.mjs",
"cy:open": "cypress open --browser --e2e --config-file=cypress.config.ts", "cy:open": "cypress open --browser --e2e --config-file=cypress.config.ts",
"cy:run": "cypress run", "cy:run": "cypress run",
"e2e": "start-server-and-test start:test http://localhost:61812 cy:run", "e2e": "start-server-and-test start:test http://localhost:61812 cy:run",
@ -27,26 +27,27 @@
"test": "npm run jest", "test": "npm run jest",
"test-and-coverage": "npm run jest-and-coverage", "test-and-coverage": "npm run jest-and-coverage",
"format": "gulp format", "format": "gulp format",
"clean": "node ./scripts/clean.js", "clean": "node ./scripts/clean.mjs",
"clean-all": "node ./scripts/clean-all.js", "clean-all": "node ./scripts/clean-all.mjs",
"cleanall": "npm run clean-all" "cleanall": "npm run clean-all"
}, },
"dependencies": { "dependencies": {
"execa": "5.1.1", "execa": "9.4.0",
"gulp": "4.0.2", "gulp": "4.0.2",
"gulp-cssnano": "2.1.3", "gulp-cssnano": "2.1.3",
"gulp-rename": "2.0.0", "gulp-rename": "2.0.0",
"gulp-replace": "1.1.3", "gulp-replace": "1.1.4",
"gulp-terser": "2.1.0", "gulp-terser": "2.1.0",
"js-yaml": "4.1.0" "js-yaml": "4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/gulp": "4.0.9", "@types/gulp": "4.0.17",
"@types/gulp-rename": "2.0.1", "@types/gulp-rename": "2.0.6",
"@typescript-eslint/parser": "5.38.0", "@typescript-eslint/parser": "8.8.1",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cypress": "10.8.0", "cypress": "13.15.0",
"start-server-and-test": "1.14.0", "start-server-and-test": "2.0.8",
"typescript": "4.8.3" "typescript": "5.6.3"
} },
"packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
} }

View File

@ -15,9 +15,6 @@
"chokidar": "^3.3.1", "chokidar": "^3.3.1",
"lodash": "^4.17.21" "lodash": "^4.17.21"
}, },
"optionalDependencies": {
"@tensorflow/tfjs-node": "3.20.0"
},
"dependencies": { "dependencies": {
"@bull-board/koa": "4.3.1", "@bull-board/koa": "4.3.1",
"@discordapp/twemoji": "14.0.2", "@discordapp/twemoji": "14.0.2",
@ -38,7 +35,7 @@
"aws-sdk": "2.1213.0", "aws-sdk": "2.1213.0",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"blurhash": "1.1.5", "blurhash": "1.1.5",
"bull": "4.9.0", "bull": "4.16.3",
"cacheable-lookup": "6.1.0", "cacheable-lookup": "6.1.0",
"cbor": "8.1.0", "cbor": "8.1.0",
"chalk": "5.0.1", "chalk": "5.0.1",
@ -81,7 +78,6 @@
"nested-property": "4.0.0", "nested-property": "4.0.0",
"node-fetch": "3.2.10", "node-fetch": "3.2.10",
"nodemailer": "6.7.8", "nodemailer": "6.7.8",
"nsfwjs": "2.4.2",
"os-utils": "0.0.14", "os-utils": "0.0.14",
"parse5": "7.1.1", "parse5": "7.1.1",
"pg": "8.8.0", "pg": "8.8.0",
@ -94,7 +90,7 @@
"qrcode": "1.5.1", "qrcode": "1.5.1",
"random-seed": "0.3.0", "random-seed": "0.3.0",
"ratelimiter": "3.4.1", "ratelimiter": "3.4.1",
"re2": "1.17.7", "re2": "1.21.4",
"redis-lock": "0.1.4", "redis-lock": "0.1.4",
"reflect-metadata": "0.1.13", "reflect-metadata": "0.1.13",
"rename": "1.0.4", "rename": "1.0.4",

View File

@ -1,16 +1,16 @@
import * as fs from 'node:fs'; import * as fs from "node:fs";
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from "node:url";
import { dirname } from 'node:path'; import { dirname } from "node:path";
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from "@nestjs/common";
import * as nsfw from 'nsfwjs'; import * as nsfw from "nsfwjs";
import si from 'systeminformation'; import si from "systeminformation";
import type { Config } from '@/config.js'; import type { Config } from "@/config.js";
import { DI } from '@/di-symbols.js'; import { DI } from "@/di-symbols.js";
const _filename = fileURLToPath(import.meta.url); const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename); const _dirname = dirname(_filename);
const REQUIRED_CPU_FLAGS = ['avx2', 'fma']; const REQUIRED_CPU_FLAGS = ["avx2", "fma"];
let isSupportedCpu: undefined | boolean = undefined; let isSupportedCpu: undefined | boolean = undefined;
@Injectable() @Injectable()
@ -20,27 +20,33 @@ export class AiService {
constructor( constructor(
@Inject(DI.config) @Inject(DI.config)
private config: Config, private config: Config,
) { ) { }
}
public async detectSensitive(path: string): Promise<nsfw.predictionType[] | null> { public async detectSensitive(
path: string,
): Promise<nsfw.predictionType[] | null> {
try { try {
if (isSupportedCpu === undefined) { if (isSupportedCpu === undefined) {
const cpuFlags = await this.getCpuFlags(); const cpuFlags = await this.getCpuFlags();
isSupportedCpu = REQUIRED_CPU_FLAGS.every(required => cpuFlags.includes(required)); isSupportedCpu = REQUIRED_CPU_FLAGS.every((required) =>
cpuFlags.includes(required),
);
} }
if (!isSupportedCpu) { if (!isSupportedCpu) {
console.error('This CPU cannot use TensorFlow.'); console.error("This CPU cannot use TensorFlow.");
return null; return null;
} }
const tf = await import('@tensorflow/tfjs-node'); const tf = await import("@tensorflow/tfjs-node");
if (this.model == null) this.model = await nsfw.load(`file://${_dirname}/../../nsfw-model/`, { size: 299 }); if (this.model == null)
this.model = await nsfw.load(`file://${_dirname}/../../nsfw-model/`, {
size: 299,
});
const buffer = await fs.promises.readFile(path); const buffer = await fs.promises.readFile(path);
const image = await tf.node.decodeImage(buffer, 3) as any; const image = (await tf.node.decodeImage(buffer, 3)) as any;
try { try {
const predictions = await this.model.classify(image); const predictions = await this.model.classify(image);
return predictions; return predictions;

View File

@ -1,248 +1,576 @@
import { Module } from '@nestjs/common'; import { Module } from "@nestjs/common";
import { DI } from '../di-symbols.js'; import { DI } from "../di-symbols.js";
import { AccountUpdateService } from './AccountUpdateService.js'; import { AccountUpdateService } from "./AccountUpdateService.js";
import { AiService } from './AiService.js'; // import { AiService } from './AiService.js';
import { AntennaService } from './AntennaService.js'; import { AntennaService } from "./AntennaService.js";
import { AppLockService } from './AppLockService.js'; import { AppLockService } from "./AppLockService.js";
import { CaptchaService } from './CaptchaService.js'; import { CaptchaService } from "./CaptchaService.js";
import { CreateNotificationService } from './CreateNotificationService.js'; import { CreateNotificationService } from "./CreateNotificationService.js";
import { CreateSystemUserService } from './CreateSystemUserService.js'; import { CreateSystemUserService } from "./CreateSystemUserService.js";
import { CustomEmojiService } from './CustomEmojiService.js'; import { CustomEmojiService } from "./CustomEmojiService.js";
import { DeleteAccountService } from './DeleteAccountService.js'; import { DeleteAccountService } from "./DeleteAccountService.js";
import { DownloadService } from './DownloadService.js'; import { DownloadService } from "./DownloadService.js";
import { DriveService } from './DriveService.js'; import { DriveService } from "./DriveService.js";
import { EmailService } from './EmailService.js'; import { EmailService } from "./EmailService.js";
import { FederatedInstanceService } from './FederatedInstanceService.js'; import { FederatedInstanceService } from "./FederatedInstanceService.js";
import { FetchInstanceMetadataService } from './FetchInstanceMetadataService.js'; import { FetchInstanceMetadataService } from "./FetchInstanceMetadataService.js";
import { GlobalEventService } from './GlobalEventService.js'; import { GlobalEventService } from "./GlobalEventService.js";
import { HashtagService } from './HashtagService.js'; import { HashtagService } from "./HashtagService.js";
import { HttpRequestService } from './HttpRequestService.js'; import { HttpRequestService } from "./HttpRequestService.js";
import { IdService } from './IdService.js'; import { IdService } from "./IdService.js";
import { ImageProcessingService } from './ImageProcessingService.js'; import { ImageProcessingService } from "./ImageProcessingService.js";
import { InstanceActorService } from './InstanceActorService.js'; import { InstanceActorService } from "./InstanceActorService.js";
import { InternalStorageService } from './InternalStorageService.js'; import { InternalStorageService } from "./InternalStorageService.js";
import { MessagingService } from './MessagingService.js'; import { MessagingService } from "./MessagingService.js";
import { MetaService } from './MetaService.js'; import { MetaService } from "./MetaService.js";
import { MfmService } from './MfmService.js'; import { MfmService } from "./MfmService.js";
import { ModerationLogService } from './ModerationLogService.js'; import { ModerationLogService } from "./ModerationLogService.js";
import { NoteCreateService } from './NoteCreateService.js'; import { NoteCreateService } from "./NoteCreateService.js";
import { NoteDeleteService } from './NoteDeleteService.js'; import { NoteDeleteService } from "./NoteDeleteService.js";
import { NotePiningService } from './NotePiningService.js'; import { NotePiningService } from "./NotePiningService.js";
import { NoteReadService } from './NoteReadService.js'; import { NoteReadService } from "./NoteReadService.js";
import { NotificationService } from './NotificationService.js'; import { NotificationService } from "./NotificationService.js";
import { PollService } from './PollService.js'; import { PollService } from "./PollService.js";
import { PushNotificationService } from './PushNotificationService.js'; import { PushNotificationService } from "./PushNotificationService.js";
import { QueryService } from './QueryService.js'; import { QueryService } from "./QueryService.js";
import { ReactionService } from './ReactionService.js'; import { ReactionService } from "./ReactionService.js";
import { RelayService } from './RelayService.js'; import { RelayService } from "./RelayService.js";
import { S3Service } from './S3Service.js'; import { S3Service } from "./S3Service.js";
import { SignupService } from './SignupService.js'; import { SignupService } from "./SignupService.js";
import { TwoFactorAuthenticationService } from './TwoFactorAuthenticationService.js'; import { TwoFactorAuthenticationService } from "./TwoFactorAuthenticationService.js";
import { UserBlockingService } from './UserBlockingService.js'; import { UserBlockingService } from "./UserBlockingService.js";
import { UserCacheService } from './UserCacheService.js'; import { UserCacheService } from "./UserCacheService.js";
import { UserFollowingService } from './UserFollowingService.js'; import { UserFollowingService } from "./UserFollowingService.js";
import { UserKeypairStoreService } from './UserKeypairStoreService.js'; import { UserKeypairStoreService } from "./UserKeypairStoreService.js";
import { UserListService } from './UserListService.js'; import { UserListService } from "./UserListService.js";
import { UserMutingService } from './UserMutingService.js'; import { UserMutingService } from "./UserMutingService.js";
import { UserSuspendService } from './UserSuspendService.js'; import { UserSuspendService } from "./UserSuspendService.js";
import { VideoProcessingService } from './VideoProcessingService.js'; import { VideoProcessingService } from "./VideoProcessingService.js";
import { WebhookService } from './WebhookService.js'; import { WebhookService } from "./WebhookService.js";
import { ProxyAccountService } from './ProxyAccountService.js'; import { ProxyAccountService } from "./ProxyAccountService.js";
import { UtilityService } from './UtilityService.js'; import { UtilityService } from "./UtilityService.js";
import { FileInfoService } from './FileInfoService.js'; import { FileInfoService } from "./FileInfoService.js";
import { ChartLoggerService } from './chart/ChartLoggerService.js'; import { ChartLoggerService } from "./chart/ChartLoggerService.js";
import FederationChart from './chart/charts/federation.js'; import FederationChart from "./chart/charts/federation.js";
import NotesChart from './chart/charts/notes.js'; import NotesChart from "./chart/charts/notes.js";
import UsersChart from './chart/charts/users.js'; import UsersChart from "./chart/charts/users.js";
import ActiveUsersChart from './chart/charts/active-users.js'; import ActiveUsersChart from "./chart/charts/active-users.js";
import InstanceChart from './chart/charts/instance.js'; import InstanceChart from "./chart/charts/instance.js";
import PerUserNotesChart from './chart/charts/per-user-notes.js'; import PerUserNotesChart from "./chart/charts/per-user-notes.js";
import DriveChart from './chart/charts/drive.js'; import DriveChart from "./chart/charts/drive.js";
import PerUserReactionsChart from './chart/charts/per-user-reactions.js'; import PerUserReactionsChart from "./chart/charts/per-user-reactions.js";
import HashtagChart from './chart/charts/hashtag.js'; import HashtagChart from "./chart/charts/hashtag.js";
import PerUserFollowingChart from './chart/charts/per-user-following.js'; import PerUserFollowingChart from "./chart/charts/per-user-following.js";
import PerUserDriveChart from './chart/charts/per-user-drive.js'; import PerUserDriveChart from "./chart/charts/per-user-drive.js";
import ApRequestChart from './chart/charts/ap-request.js'; import ApRequestChart from "./chart/charts/ap-request.js";
import { ChartManagementService } from './chart/ChartManagementService.js'; import { ChartManagementService } from "./chart/ChartManagementService.js";
import { AbuseUserReportEntityService } from './entities/AbuseUserReportEntityService.js'; import { AbuseUserReportEntityService } from "./entities/AbuseUserReportEntityService.js";
import { AntennaEntityService } from './entities/AntennaEntityService.js'; import { AntennaEntityService } from "./entities/AntennaEntityService.js";
import { AppEntityService } from './entities/AppEntityService.js'; import { AppEntityService } from "./entities/AppEntityService.js";
import { AuthSessionEntityService } from './entities/AuthSessionEntityService.js'; import { AuthSessionEntityService } from "./entities/AuthSessionEntityService.js";
import { BlockingEntityService } from './entities/BlockingEntityService.js'; import { BlockingEntityService } from "./entities/BlockingEntityService.js";
import { ChannelEntityService } from './entities/ChannelEntityService.js'; import { ChannelEntityService } from "./entities/ChannelEntityService.js";
import { ClipEntityService } from './entities/ClipEntityService.js'; import { ClipEntityService } from "./entities/ClipEntityService.js";
import { DriveFileEntityService } from './entities/DriveFileEntityService.js'; import { DriveFileEntityService } from "./entities/DriveFileEntityService.js";
import { DriveFolderEntityService } from './entities/DriveFolderEntityService.js'; import { DriveFolderEntityService } from "./entities/DriveFolderEntityService.js";
import { EmojiEntityService } from './entities/EmojiEntityService.js'; import { EmojiEntityService } from "./entities/EmojiEntityService.js";
import { FollowingEntityService } from './entities/FollowingEntityService.js'; import { FollowingEntityService } from "./entities/FollowingEntityService.js";
import { FollowRequestEntityService } from './entities/FollowRequestEntityService.js'; import { FollowRequestEntityService } from "./entities/FollowRequestEntityService.js";
import { GalleryLikeEntityService } from './entities/GalleryLikeEntityService.js'; import { GalleryLikeEntityService } from "./entities/GalleryLikeEntityService.js";
import { GalleryPostEntityService } from './entities/GalleryPostEntityService.js'; import { GalleryPostEntityService } from "./entities/GalleryPostEntityService.js";
import { HashtagEntityService } from './entities/HashtagEntityService.js'; import { HashtagEntityService } from "./entities/HashtagEntityService.js";
import { InstanceEntityService } from './entities/InstanceEntityService.js'; import { InstanceEntityService } from "./entities/InstanceEntityService.js";
import { MessagingMessageEntityService } from './entities/MessagingMessageEntityService.js'; import { MessagingMessageEntityService } from "./entities/MessagingMessageEntityService.js";
import { ModerationLogEntityService } from './entities/ModerationLogEntityService.js'; import { ModerationLogEntityService } from "./entities/ModerationLogEntityService.js";
import { MutingEntityService } from './entities/MutingEntityService.js'; import { MutingEntityService } from "./entities/MutingEntityService.js";
import { NoteEntityService } from './entities/NoteEntityService.js'; import { NoteEntityService } from "./entities/NoteEntityService.js";
import { NoteFavoriteEntityService } from './entities/NoteFavoriteEntityService.js'; import { NoteFavoriteEntityService } from "./entities/NoteFavoriteEntityService.js";
import { NoteReactionEntityService } from './entities/NoteReactionEntityService.js'; import { NoteReactionEntityService } from "./entities/NoteReactionEntityService.js";
import { NotificationEntityService } from './entities/NotificationEntityService.js'; import { NotificationEntityService } from "./entities/NotificationEntityService.js";
import { PageEntityService } from './entities/PageEntityService.js'; import { PageEntityService } from "./entities/PageEntityService.js";
import { PageLikeEntityService } from './entities/PageLikeEntityService.js'; import { PageLikeEntityService } from "./entities/PageLikeEntityService.js";
import { SigninEntityService } from './entities/SigninEntityService.js'; import { SigninEntityService } from "./entities/SigninEntityService.js";
import { UserEntityService } from './entities/UserEntityService.js'; import { UserEntityService } from "./entities/UserEntityService.js";
import { UserGroupEntityService } from './entities/UserGroupEntityService.js'; import { UserGroupEntityService } from "./entities/UserGroupEntityService.js";
import { UserGroupInvitationEntityService } from './entities/UserGroupInvitationEntityService.js'; import { UserGroupInvitationEntityService } from "./entities/UserGroupInvitationEntityService.js";
import { UserListEntityService } from './entities/UserListEntityService.js'; import { UserListEntityService } from "./entities/UserListEntityService.js";
import { ApAudienceService } from './remote/activitypub/ApAudienceService.js'; import { ApAudienceService } from "./remote/activitypub/ApAudienceService.js";
import { ApDbResolverService } from './remote/activitypub/ApDbResolverService.js'; import { ApDbResolverService } from "./remote/activitypub/ApDbResolverService.js";
import { ApDeliverManagerService } from './remote/activitypub/ApDeliverManagerService.js'; import { ApDeliverManagerService } from "./remote/activitypub/ApDeliverManagerService.js";
import { ApInboxService } from './remote/activitypub/ApInboxService.js'; import { ApInboxService } from "./remote/activitypub/ApInboxService.js";
import { ApLoggerService } from './remote/activitypub/ApLoggerService.js'; import { ApLoggerService } from "./remote/activitypub/ApLoggerService.js";
import { ApMfmService } from './remote/activitypub/ApMfmService.js'; import { ApMfmService } from "./remote/activitypub/ApMfmService.js";
import { ApRendererService } from './remote/activitypub/ApRendererService.js'; import { ApRendererService } from "./remote/activitypub/ApRendererService.js";
import { ApRequestService } from './remote/activitypub/ApRequestService.js'; import { ApRequestService } from "./remote/activitypub/ApRequestService.js";
import { ApResolverService } from './remote/activitypub/ApResolverService.js'; import { ApResolverService } from "./remote/activitypub/ApResolverService.js";
import { LdSignatureService } from './remote/activitypub/LdSignatureService.js'; import { LdSignatureService } from "./remote/activitypub/LdSignatureService.js";
import { RemoteLoggerService } from './remote/RemoteLoggerService.js'; import { RemoteLoggerService } from "./remote/RemoteLoggerService.js";
import { ResolveUserService } from './remote/ResolveUserService.js'; import { ResolveUserService } from "./remote/ResolveUserService.js";
import { WebfingerService } from './remote/WebfingerService.js'; import { WebfingerService } from "./remote/WebfingerService.js";
import { ApImageService } from './remote/activitypub/models/ApImageService.js'; import { ApImageService } from "./remote/activitypub/models/ApImageService.js";
import { ApMentionService } from './remote/activitypub/models/ApMentionService.js'; import { ApMentionService } from "./remote/activitypub/models/ApMentionService.js";
import { ApNoteService } from './remote/activitypub/models/ApNoteService.js'; import { ApNoteService } from "./remote/activitypub/models/ApNoteService.js";
import { ApPersonService } from './remote/activitypub/models/ApPersonService.js'; import { ApPersonService } from "./remote/activitypub/models/ApPersonService.js";
import { ApQuestionService } from './remote/activitypub/models/ApQuestionService.js'; import { ApQuestionService } from "./remote/activitypub/models/ApQuestionService.js";
import { QueueModule } from './queue/QueueModule.js'; import { QueueModule } from "./queue/QueueModule.js";
import { QueueService } from './QueueService.js'; import { QueueService } from "./QueueService.js";
import { LoggerService } from './LoggerService.js'; import { LoggerService } from "./LoggerService.js";
import type { Provider } from '@nestjs/common'; import type { Provider } from "@nestjs/common";
//#region 文字列ベースでのinjection用(循環参照対応のため) //#region 文字列ベースでのinjection用(循環参照対応のため)
const $LoggerService: Provider = { provide: 'LoggerService', useExisting: LoggerService }; const $LoggerService: Provider = {
const $AccountUpdateService: Provider = { provide: 'AccountUpdateService', useExisting: AccountUpdateService }; provide: "LoggerService",
const $AiService: Provider = { provide: 'AiService', useExisting: AiService }; useExisting: LoggerService,
const $AntennaService: Provider = { provide: 'AntennaService', useExisting: AntennaService }; };
const $AppLockService: Provider = { provide: 'AppLockService', useExisting: AppLockService }; const $AccountUpdateService: Provider = {
const $CaptchaService: Provider = { provide: 'CaptchaService', useExisting: CaptchaService }; provide: "AccountUpdateService",
const $CreateNotificationService: Provider = { provide: 'CreateNotificationService', useExisting: CreateNotificationService }; useExisting: AccountUpdateService,
const $CreateSystemUserService: Provider = { provide: 'CreateSystemUserService', useExisting: CreateSystemUserService }; };
const $CustomEmojiService: Provider = { provide: 'CustomEmojiService', useExisting: CustomEmojiService }; // const $AiService: Provider = { provide: "AiService", useExisting: AiService };
const $DeleteAccountService: Provider = { provide: 'DeleteAccountService', useExisting: DeleteAccountService }; const $AntennaService: Provider = {
const $DownloadService: Provider = { provide: 'DownloadService', useExisting: DownloadService }; provide: "AntennaService",
const $DriveService: Provider = { provide: 'DriveService', useExisting: DriveService }; useExisting: AntennaService,
const $EmailService: Provider = { provide: 'EmailService', useExisting: EmailService }; };
const $FederatedInstanceService: Provider = { provide: 'FederatedInstanceService', useExisting: FederatedInstanceService }; const $AppLockService: Provider = {
const $FetchInstanceMetadataService: Provider = { provide: 'FetchInstanceMetadataService', useExisting: FetchInstanceMetadataService }; provide: "AppLockService",
const $GlobalEventService: Provider = { provide: 'GlobalEventService', useExisting: GlobalEventService }; useExisting: AppLockService,
const $HashtagService: Provider = { provide: 'HashtagService', useExisting: HashtagService }; };
const $HttpRequestService: Provider = { provide: 'HttpRequestService', useExisting: HttpRequestService }; const $CaptchaService: Provider = {
const $IdService: Provider = { provide: 'IdService', useExisting: IdService }; provide: "CaptchaService",
const $ImageProcessingService: Provider = { provide: 'ImageProcessingService', useExisting: ImageProcessingService }; useExisting: CaptchaService,
const $InstanceActorService: Provider = { provide: 'InstanceActorService', useExisting: InstanceActorService }; };
const $InternalStorageService: Provider = { provide: 'InternalStorageService', useExisting: InternalStorageService }; const $CreateNotificationService: Provider = {
const $MessagingService: Provider = { provide: 'MessagingService', useExisting: MessagingService }; provide: "CreateNotificationService",
const $MetaService: Provider = { provide: 'MetaService', useExisting: MetaService }; useExisting: CreateNotificationService,
const $MfmService: Provider = { provide: 'MfmService', useExisting: MfmService }; };
const $ModerationLogService: Provider = { provide: 'ModerationLogService', useExisting: ModerationLogService }; const $CreateSystemUserService: Provider = {
const $NoteCreateService: Provider = { provide: 'NoteCreateService', useExisting: NoteCreateService }; provide: "CreateSystemUserService",
const $NoteDeleteService: Provider = { provide: 'NoteDeleteService', useExisting: NoteDeleteService }; useExisting: CreateSystemUserService,
const $NotePiningService: Provider = { provide: 'NotePiningService', useExisting: NotePiningService }; };
const $NoteReadService: Provider = { provide: 'NoteReadService', useExisting: NoteReadService }; const $CustomEmojiService: Provider = {
const $NotificationService: Provider = { provide: 'NotificationService', useExisting: NotificationService }; provide: "CustomEmojiService",
const $PollService: Provider = { provide: 'PollService', useExisting: PollService }; useExisting: CustomEmojiService,
const $ProxyAccountService: Provider = { provide: 'ProxyAccountService', useExisting: ProxyAccountService }; };
const $PushNotificationService: Provider = { provide: 'PushNotificationService', useExisting: PushNotificationService }; const $DeleteAccountService: Provider = {
const $QueryService: Provider = { provide: 'QueryService', useExisting: QueryService }; provide: "DeleteAccountService",
const $ReactionService: Provider = { provide: 'ReactionService', useExisting: ReactionService }; useExisting: DeleteAccountService,
const $RelayService: Provider = { provide: 'RelayService', useExisting: RelayService }; };
const $S3Service: Provider = { provide: 'S3Service', useExisting: S3Service }; const $DownloadService: Provider = {
const $SignupService: Provider = { provide: 'SignupService', useExisting: SignupService }; provide: "DownloadService",
const $TwoFactorAuthenticationService: Provider = { provide: 'TwoFactorAuthenticationService', useExisting: TwoFactorAuthenticationService }; useExisting: DownloadService,
const $UserBlockingService: Provider = { provide: 'UserBlockingService', useExisting: UserBlockingService }; };
const $UserCacheService: Provider = { provide: 'UserCacheService', useExisting: UserCacheService }; const $DriveService: Provider = {
const $UserFollowingService: Provider = { provide: 'UserFollowingService', useExisting: UserFollowingService }; provide: "DriveService",
const $UserKeypairStoreService: Provider = { provide: 'UserKeypairStoreService', useExisting: UserKeypairStoreService }; useExisting: DriveService,
const $UserListService: Provider = { provide: 'UserListService', useExisting: UserListService }; };
const $UserMutingService: Provider = { provide: 'UserMutingService', useExisting: UserMutingService }; const $EmailService: Provider = {
const $UserSuspendService: Provider = { provide: 'UserSuspendService', useExisting: UserSuspendService }; provide: "EmailService",
const $VideoProcessingService: Provider = { provide: 'VideoProcessingService', useExisting: VideoProcessingService }; useExisting: EmailService,
const $WebhookService: Provider = { provide: 'WebhookService', useExisting: WebhookService }; };
const $UtilityService: Provider = { provide: 'UtilityService', useExisting: UtilityService }; const $FederatedInstanceService: Provider = {
const $FileInfoService: Provider = { provide: 'FileInfoService', useExisting: FileInfoService }; provide: "FederatedInstanceService",
const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService }; useExisting: FederatedInstanceService,
const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart }; };
const $NotesChart: Provider = { provide: 'NotesChart', useExisting: NotesChart }; const $FetchInstanceMetadataService: Provider = {
const $UsersChart: Provider = { provide: 'UsersChart', useExisting: UsersChart }; provide: "FetchInstanceMetadataService",
const $ActiveUsersChart: Provider = { provide: 'ActiveUsersChart', useExisting: ActiveUsersChart }; useExisting: FetchInstanceMetadataService,
const $InstanceChart: Provider = { provide: 'InstanceChart', useExisting: InstanceChart }; };
const $PerUserNotesChart: Provider = { provide: 'PerUserNotesChart', useExisting: PerUserNotesChart }; const $GlobalEventService: Provider = {
const $DriveChart: Provider = { provide: 'DriveChart', useExisting: DriveChart }; provide: "GlobalEventService",
const $PerUserReactionsChart: Provider = { provide: 'PerUserReactionsChart', useExisting: PerUserReactionsChart }; useExisting: GlobalEventService,
const $HashtagChart: Provider = { provide: 'HashtagChart', useExisting: HashtagChart }; };
const $PerUserFollowingChart: Provider = { provide: 'PerUserFollowingChart', useExisting: PerUserFollowingChart }; const $HashtagService: Provider = {
const $PerUserDriveChart: Provider = { provide: 'PerUserDriveChart', useExisting: PerUserDriveChart }; provide: "HashtagService",
const $ApRequestChart: Provider = { provide: 'ApRequestChart', useExisting: ApRequestChart }; useExisting: HashtagService,
const $ChartManagementService: Provider = { provide: 'ChartManagementService', useExisting: ChartManagementService }; };
const $HttpRequestService: Provider = {
provide: "HttpRequestService",
useExisting: HttpRequestService,
};
const $IdService: Provider = { provide: "IdService", useExisting: IdService };
const $ImageProcessingService: Provider = {
provide: "ImageProcessingService",
useExisting: ImageProcessingService,
};
const $InstanceActorService: Provider = {
provide: "InstanceActorService",
useExisting: InstanceActorService,
};
const $InternalStorageService: Provider = {
provide: "InternalStorageService",
useExisting: InternalStorageService,
};
const $MessagingService: Provider = {
provide: "MessagingService",
useExisting: MessagingService,
};
const $MetaService: Provider = {
provide: "MetaService",
useExisting: MetaService,
};
const $MfmService: Provider = {
provide: "MfmService",
useExisting: MfmService,
};
const $ModerationLogService: Provider = {
provide: "ModerationLogService",
useExisting: ModerationLogService,
};
const $NoteCreateService: Provider = {
provide: "NoteCreateService",
useExisting: NoteCreateService,
};
const $NoteDeleteService: Provider = {
provide: "NoteDeleteService",
useExisting: NoteDeleteService,
};
const $NotePiningService: Provider = {
provide: "NotePiningService",
useExisting: NotePiningService,
};
const $NoteReadService: Provider = {
provide: "NoteReadService",
useExisting: NoteReadService,
};
const $NotificationService: Provider = {
provide: "NotificationService",
useExisting: NotificationService,
};
const $PollService: Provider = {
provide: "PollService",
useExisting: PollService,
};
const $ProxyAccountService: Provider = {
provide: "ProxyAccountService",
useExisting: ProxyAccountService,
};
const $PushNotificationService: Provider = {
provide: "PushNotificationService",
useExisting: PushNotificationService,
};
const $QueryService: Provider = {
provide: "QueryService",
useExisting: QueryService,
};
const $ReactionService: Provider = {
provide: "ReactionService",
useExisting: ReactionService,
};
const $RelayService: Provider = {
provide: "RelayService",
useExisting: RelayService,
};
const $S3Service: Provider = { provide: "S3Service", useExisting: S3Service };
const $SignupService: Provider = {
provide: "SignupService",
useExisting: SignupService,
};
const $TwoFactorAuthenticationService: Provider = {
provide: "TwoFactorAuthenticationService",
useExisting: TwoFactorAuthenticationService,
};
const $UserBlockingService: Provider = {
provide: "UserBlockingService",
useExisting: UserBlockingService,
};
const $UserCacheService: Provider = {
provide: "UserCacheService",
useExisting: UserCacheService,
};
const $UserFollowingService: Provider = {
provide: "UserFollowingService",
useExisting: UserFollowingService,
};
const $UserKeypairStoreService: Provider = {
provide: "UserKeypairStoreService",
useExisting: UserKeypairStoreService,
};
const $UserListService: Provider = {
provide: "UserListService",
useExisting: UserListService,
};
const $UserMutingService: Provider = {
provide: "UserMutingService",
useExisting: UserMutingService,
};
const $UserSuspendService: Provider = {
provide: "UserSuspendService",
useExisting: UserSuspendService,
};
const $VideoProcessingService: Provider = {
provide: "VideoProcessingService",
useExisting: VideoProcessingService,
};
const $WebhookService: Provider = {
provide: "WebhookService",
useExisting: WebhookService,
};
const $UtilityService: Provider = {
provide: "UtilityService",
useExisting: UtilityService,
};
const $FileInfoService: Provider = {
provide: "FileInfoService",
useExisting: FileInfoService,
};
const $ChartLoggerService: Provider = {
provide: "ChartLoggerService",
useExisting: ChartLoggerService,
};
const $FederationChart: Provider = {
provide: "FederationChart",
useExisting: FederationChart,
};
const $NotesChart: Provider = {
provide: "NotesChart",
useExisting: NotesChart,
};
const $UsersChart: Provider = {
provide: "UsersChart",
useExisting: UsersChart,
};
const $ActiveUsersChart: Provider = {
provide: "ActiveUsersChart",
useExisting: ActiveUsersChart,
};
const $InstanceChart: Provider = {
provide: "InstanceChart",
useExisting: InstanceChart,
};
const $PerUserNotesChart: Provider = {
provide: "PerUserNotesChart",
useExisting: PerUserNotesChart,
};
const $DriveChart: Provider = {
provide: "DriveChart",
useExisting: DriveChart,
};
const $PerUserReactionsChart: Provider = {
provide: "PerUserReactionsChart",
useExisting: PerUserReactionsChart,
};
const $HashtagChart: Provider = {
provide: "HashtagChart",
useExisting: HashtagChart,
};
const $PerUserFollowingChart: Provider = {
provide: "PerUserFollowingChart",
useExisting: PerUserFollowingChart,
};
const $PerUserDriveChart: Provider = {
provide: "PerUserDriveChart",
useExisting: PerUserDriveChart,
};
const $ApRequestChart: Provider = {
provide: "ApRequestChart",
useExisting: ApRequestChart,
};
const $ChartManagementService: Provider = {
provide: "ChartManagementService",
useExisting: ChartManagementService,
};
const $AbuseUserReportEntityService: Provider = { provide: 'AbuseUserReportEntityService', useExisting: AbuseUserReportEntityService }; const $AbuseUserReportEntityService: Provider = {
const $AntennaEntityService: Provider = { provide: 'AntennaEntityService', useExisting: AntennaEntityService }; provide: "AbuseUserReportEntityService",
const $AppEntityService: Provider = { provide: 'AppEntityService', useExisting: AppEntityService }; useExisting: AbuseUserReportEntityService,
const $AuthSessionEntityService: Provider = { provide: 'AuthSessionEntityService', useExisting: AuthSessionEntityService }; };
const $BlockingEntityService: Provider = { provide: 'BlockingEntityService', useExisting: BlockingEntityService }; const $AntennaEntityService: Provider = {
const $ChannelEntityService: Provider = { provide: 'ChannelEntityService', useExisting: ChannelEntityService }; provide: "AntennaEntityService",
const $ClipEntityService: Provider = { provide: 'ClipEntityService', useExisting: ClipEntityService }; useExisting: AntennaEntityService,
const $DriveFileEntityService: Provider = { provide: 'DriveFileEntityService', useExisting: DriveFileEntityService }; };
const $DriveFolderEntityService: Provider = { provide: 'DriveFolderEntityService', useExisting: DriveFolderEntityService }; const $AppEntityService: Provider = {
const $EmojiEntityService: Provider = { provide: 'EmojiEntityService', useExisting: EmojiEntityService }; provide: "AppEntityService",
const $FollowingEntityService: Provider = { provide: 'FollowingEntityService', useExisting: FollowingEntityService }; useExisting: AppEntityService,
const $FollowRequestEntityService: Provider = { provide: 'FollowRequestEntityService', useExisting: FollowRequestEntityService }; };
const $GalleryLikeEntityService: Provider = { provide: 'GalleryLikeEntityService', useExisting: GalleryLikeEntityService }; const $AuthSessionEntityService: Provider = {
const $GalleryPostEntityService: Provider = { provide: 'GalleryPostEntityService', useExisting: GalleryPostEntityService }; provide: "AuthSessionEntityService",
const $HashtagEntityService: Provider = { provide: 'HashtagEntityService', useExisting: HashtagEntityService }; useExisting: AuthSessionEntityService,
const $InstanceEntityService: Provider = { provide: 'InstanceEntityService', useExisting: InstanceEntityService }; };
const $MessagingMessageEntityService: Provider = { provide: 'MessagingMessageEntityService', useExisting: MessagingMessageEntityService }; const $BlockingEntityService: Provider = {
const $ModerationLogEntityService: Provider = { provide: 'ModerationLogEntityService', useExisting: ModerationLogEntityService }; provide: "BlockingEntityService",
const $MutingEntityService: Provider = { provide: 'MutingEntityService', useExisting: MutingEntityService }; useExisting: BlockingEntityService,
const $NoteEntityService: Provider = { provide: 'NoteEntityService', useExisting: NoteEntityService }; };
const $NoteFavoriteEntityService: Provider = { provide: 'NoteFavoriteEntityService', useExisting: NoteFavoriteEntityService }; const $ChannelEntityService: Provider = {
const $NoteReactionEntityService: Provider = { provide: 'NoteReactionEntityService', useExisting: NoteReactionEntityService }; provide: "ChannelEntityService",
const $NotificationEntityService: Provider = { provide: 'NotificationEntityService', useExisting: NotificationEntityService }; useExisting: ChannelEntityService,
const $PageEntityService: Provider = { provide: 'PageEntityService', useExisting: PageEntityService }; };
const $PageLikeEntityService: Provider = { provide: 'PageLikeEntityService', useExisting: PageLikeEntityService }; const $ClipEntityService: Provider = {
const $SigninEntityService: Provider = { provide: 'SigninEntityService', useExisting: SigninEntityService }; provide: "ClipEntityService",
const $UserEntityService: Provider = { provide: 'UserEntityService', useExisting: UserEntityService }; useExisting: ClipEntityService,
const $UserGroupEntityService: Provider = { provide: 'UserGroupEntityService', useExisting: UserGroupEntityService }; };
const $UserGroupInvitationEntityService: Provider = { provide: 'UserGroupInvitationEntityService', useExisting: UserGroupInvitationEntityService }; const $DriveFileEntityService: Provider = {
const $UserListEntityService: Provider = { provide: 'UserListEntityService', useExisting: UserListEntityService }; provide: "DriveFileEntityService",
useExisting: DriveFileEntityService,
};
const $DriveFolderEntityService: Provider = {
provide: "DriveFolderEntityService",
useExisting: DriveFolderEntityService,
};
const $EmojiEntityService: Provider = {
provide: "EmojiEntityService",
useExisting: EmojiEntityService,
};
const $FollowingEntityService: Provider = {
provide: "FollowingEntityService",
useExisting: FollowingEntityService,
};
const $FollowRequestEntityService: Provider = {
provide: "FollowRequestEntityService",
useExisting: FollowRequestEntityService,
};
const $GalleryLikeEntityService: Provider = {
provide: "GalleryLikeEntityService",
useExisting: GalleryLikeEntityService,
};
const $GalleryPostEntityService: Provider = {
provide: "GalleryPostEntityService",
useExisting: GalleryPostEntityService,
};
const $HashtagEntityService: Provider = {
provide: "HashtagEntityService",
useExisting: HashtagEntityService,
};
const $InstanceEntityService: Provider = {
provide: "InstanceEntityService",
useExisting: InstanceEntityService,
};
const $MessagingMessageEntityService: Provider = {
provide: "MessagingMessageEntityService",
useExisting: MessagingMessageEntityService,
};
const $ModerationLogEntityService: Provider = {
provide: "ModerationLogEntityService",
useExisting: ModerationLogEntityService,
};
const $MutingEntityService: Provider = {
provide: "MutingEntityService",
useExisting: MutingEntityService,
};
const $NoteEntityService: Provider = {
provide: "NoteEntityService",
useExisting: NoteEntityService,
};
const $NoteFavoriteEntityService: Provider = {
provide: "NoteFavoriteEntityService",
useExisting: NoteFavoriteEntityService,
};
const $NoteReactionEntityService: Provider = {
provide: "NoteReactionEntityService",
useExisting: NoteReactionEntityService,
};
const $NotificationEntityService: Provider = {
provide: "NotificationEntityService",
useExisting: NotificationEntityService,
};
const $PageEntityService: Provider = {
provide: "PageEntityService",
useExisting: PageEntityService,
};
const $PageLikeEntityService: Provider = {
provide: "PageLikeEntityService",
useExisting: PageLikeEntityService,
};
const $SigninEntityService: Provider = {
provide: "SigninEntityService",
useExisting: SigninEntityService,
};
const $UserEntityService: Provider = {
provide: "UserEntityService",
useExisting: UserEntityService,
};
const $UserGroupEntityService: Provider = {
provide: "UserGroupEntityService",
useExisting: UserGroupEntityService,
};
const $UserGroupInvitationEntityService: Provider = {
provide: "UserGroupInvitationEntityService",
useExisting: UserGroupInvitationEntityService,
};
const $UserListEntityService: Provider = {
provide: "UserListEntityService",
useExisting: UserListEntityService,
};
const $ApAudienceService: Provider = { provide: 'ApAudienceService', useExisting: ApAudienceService }; const $ApAudienceService: Provider = {
const $ApDbResolverService: Provider = { provide: 'ApDbResolverService', useExisting: ApDbResolverService }; provide: "ApAudienceService",
const $ApDeliverManagerService: Provider = { provide: 'ApDeliverManagerService', useExisting: ApDeliverManagerService }; useExisting: ApAudienceService,
const $ApInboxService: Provider = { provide: 'ApInboxService', useExisting: ApInboxService }; };
const $ApLoggerService: Provider = { provide: 'ApLoggerService', useExisting: ApLoggerService }; const $ApDbResolverService: Provider = {
const $ApMfmService: Provider = { provide: 'ApMfmService', useExisting: ApMfmService }; provide: "ApDbResolverService",
const $ApRendererService: Provider = { provide: 'ApRendererService', useExisting: ApRendererService }; useExisting: ApDbResolverService,
const $ApRequestService: Provider = { provide: 'ApRequestService', useExisting: ApRequestService }; };
const $ApResolverService: Provider = { provide: 'ApResolverService', useExisting: ApResolverService }; const $ApDeliverManagerService: Provider = {
const $LdSignatureService: Provider = { provide: 'LdSignatureService', useExisting: LdSignatureService }; provide: "ApDeliverManagerService",
const $RemoteLoggerService: Provider = { provide: 'RemoteLoggerService', useExisting: RemoteLoggerService }; useExisting: ApDeliverManagerService,
const $ResolveUserService: Provider = { provide: 'ResolveUserService', useExisting: ResolveUserService }; };
const $WebfingerService: Provider = { provide: 'WebfingerService', useExisting: WebfingerService }; const $ApInboxService: Provider = {
const $ApImageService: Provider = { provide: 'ApImageService', useExisting: ApImageService }; provide: "ApInboxService",
const $ApMentionService: Provider = { provide: 'ApMentionService', useExisting: ApMentionService }; useExisting: ApInboxService,
const $ApNoteService: Provider = { provide: 'ApNoteService', useExisting: ApNoteService }; };
const $ApPersonService: Provider = { provide: 'ApPersonService', useExisting: ApPersonService }; const $ApLoggerService: Provider = {
const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting: ApQuestionService }; provide: "ApLoggerService",
useExisting: ApLoggerService,
};
const $ApMfmService: Provider = {
provide: "ApMfmService",
useExisting: ApMfmService,
};
const $ApRendererService: Provider = {
provide: "ApRendererService",
useExisting: ApRendererService,
};
const $ApRequestService: Provider = {
provide: "ApRequestService",
useExisting: ApRequestService,
};
const $ApResolverService: Provider = {
provide: "ApResolverService",
useExisting: ApResolverService,
};
const $LdSignatureService: Provider = {
provide: "LdSignatureService",
useExisting: LdSignatureService,
};
const $RemoteLoggerService: Provider = {
provide: "RemoteLoggerService",
useExisting: RemoteLoggerService,
};
const $ResolveUserService: Provider = {
provide: "ResolveUserService",
useExisting: ResolveUserService,
};
const $WebfingerService: Provider = {
provide: "WebfingerService",
useExisting: WebfingerService,
};
const $ApImageService: Provider = {
provide: "ApImageService",
useExisting: ApImageService,
};
const $ApMentionService: Provider = {
provide: "ApMentionService",
useExisting: ApMentionService,
};
const $ApNoteService: Provider = {
provide: "ApNoteService",
useExisting: ApNoteService,
};
const $ApPersonService: Provider = {
provide: "ApPersonService",
useExisting: ApPersonService,
};
const $ApQuestionService: Provider = {
provide: "ApQuestionService",
useExisting: ApQuestionService,
};
//#endregion //#endregion
@Module({ @Module({
imports: [ imports: [QueueModule],
QueueModule,
],
providers: [ providers: [
LoggerService, LoggerService,
AccountUpdateService, AccountUpdateService,
AiService, // AiService,
AntennaService, AntennaService,
AppLockService, AppLockService,
CaptchaService, CaptchaService,
@ -358,7 +686,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
//#region 文字列ベースでのinjection用(循環参照対応のため) //#region 文字列ベースでのinjection用(循環参照対応のため)
$LoggerService, $LoggerService,
$AccountUpdateService, $AccountUpdateService,
$AiService, // $AiService,
$AntennaService, $AntennaService,
$AppLockService, $AppLockService,
$CaptchaService, $CaptchaService,
@ -475,7 +803,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
QueueModule, QueueModule,
LoggerService, LoggerService,
AccountUpdateService, AccountUpdateService,
AiService, // AiService,
AntennaService, AntennaService,
AppLockService, AppLockService,
CaptchaService, CaptchaService,
@ -590,7 +918,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
//#region 文字列ベースでのinjection用(循環参照対応のため) //#region 文字列ベースでのinjection用(循環参照対応のため)
$LoggerService, $LoggerService,
$AccountUpdateService, $AccountUpdateService,
$AiService, // $AiService,
$AntennaService, $AntennaService,
$AppLockService, $AppLockService,
$CaptchaService, $CaptchaService,
@ -703,4 +1031,4 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
//#endregion //#endregion
], ],
}) })
export class CoreModule {} export class CoreModule { }

View File

@ -1,19 +1,19 @@
import * as fs from 'node:fs'; import * as fs from "node:fs";
import * as crypto from 'node:crypto'; import * as crypto from "node:crypto";
import { join } from 'node:path'; import { join } from "node:path";
import * as stream from 'node:stream'; import * as stream from "node:stream";
import * as util from 'node:util'; import * as util from "node:util";
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from "@nestjs/common";
import { FSWatcher } from 'chokidar'; import { FSWatcher } from "chokidar";
import { fileTypeFromFile } from 'file-type'; import { fileTypeFromFile } from "file-type";
import FFmpeg from 'fluent-ffmpeg'; import FFmpeg from "fluent-ffmpeg";
import isSvg from 'is-svg'; import isSvg from "is-svg";
import probeImageSize from 'probe-image-size'; import probeImageSize from "probe-image-size";
import { type predictionType } from 'nsfwjs'; import { type predictionType } from "nsfwjs";
import sharp from 'sharp'; import sharp from "sharp";
import { encode } from 'blurhash'; import { encode } from "blurhash";
import { createTempDir } from '@/misc/create-temp.js'; import { createTempDir } from "@/misc/create-temp.js";
import { AiService } from '@/core/AiService.js'; // import { AiService } from '@/core/AiService.js';
const pipeline = util.promisify(stream.pipeline); const pipeline = util.promisify(stream.pipeline);
@ -34,30 +34,30 @@ export type FileInfo = {
}; };
const TYPE_OCTET_STREAM = { const TYPE_OCTET_STREAM = {
mime: 'application/octet-stream', mime: "application/octet-stream",
ext: null, ext: null,
}; };
const TYPE_SVG = { const TYPE_SVG = {
mime: 'image/svg+xml', mime: "image/svg+xml",
ext: 'svg', ext: "svg",
}; };
@Injectable() @Injectable()
export class FileInfoService { export class FileInfoService {
constructor( constructor() {} // private aiService: AiService,
private aiService: AiService,
) {
}
/** /**
* Get file information * Get file information
*/ */
public async getFileInfo(path: string, opts: { public async getFileInfo(
skipSensitiveDetection: boolean; path: string,
sensitiveThreshold?: number; opts: {
sensitiveThresholdForPorn?: number; skipSensitiveDetection: boolean;
enableSensitiveMediaDetectionForVideos?: boolean; sensitiveThreshold?: number;
}): Promise<FileInfo> { sensitiveThresholdForPorn?: number;
enableSensitiveMediaDetectionForVideos?: boolean;
},
): Promise<FileInfo> {
const warnings = [] as string[]; const warnings = [] as string[];
const size = await this.getFileSize(path); const size = await this.getFileSize(path);
@ -70,24 +70,36 @@ export class FileInfoService {
let height: number | undefined; let height: number | undefined;
let orientation: number | undefined; let orientation: number | undefined;
if (['image/jpeg', 'image/gif', 'image/png', 'image/apng', 'image/webp', 'image/bmp', 'image/tiff', 'image/svg+xml', 'image/vnd.adobe.photoshop'].includes(type.mime)) { if (
const imageSize = await this.detectImageSize(path).catch(e => { [
"image/jpeg",
"image/gif",
"image/png",
"image/apng",
"image/webp",
"image/bmp",
"image/tiff",
"image/svg+xml",
"image/vnd.adobe.photoshop",
].includes(type.mime)
) {
const imageSize = await this.detectImageSize(path).catch((e) => {
warnings.push(`detectImageSize failed: ${e}`); warnings.push(`detectImageSize failed: ${e}`);
return undefined; return undefined;
}); });
// うまく判定できない画像は octet-stream にする // うまく判定できない画像は octet-stream にする
if (!imageSize) { if (!imageSize) {
warnings.push('cannot detect image dimensions'); warnings.push("cannot detect image dimensions");
type = TYPE_OCTET_STREAM; type = TYPE_OCTET_STREAM;
} else if (imageSize.wUnits === 'px') { } else if (imageSize.wUnits === "px") {
width = imageSize.width; width = imageSize.width;
height = imageSize.height; height = imageSize.height;
orientation = imageSize.orientation; orientation = imageSize.orientation;
// 制限を超えている画像は octet-stream にする // 制限を超えている画像は octet-stream にする
if (imageSize.width > 16383 || imageSize.height > 16383) { if (imageSize.width > 16383 || imageSize.height > 16383) {
warnings.push('image dimensions exceeds limits'); warnings.push("image dimensions exceeds limits");
type = TYPE_OCTET_STREAM; type = TYPE_OCTET_STREAM;
} }
} else { } else {
@ -97,8 +109,17 @@ export class FileInfoService {
let blurhash: string | undefined; let blurhash: string | undefined;
if (['image/jpeg', 'image/gif', 'image/png', 'image/apng', 'image/webp', 'image/svg+xml'].includes(type.mime)) { if (
blurhash = await this.getBlurhash(path).catch(e => { [
"image/jpeg",
"image/gif",
"image/png",
"image/apng",
"image/webp",
"image/svg+xml",
].includes(type.mime)
) {
blurhash = await this.getBlurhash(path).catch((e) => {
warnings.push(`getBlurhash failed: ${e}`); warnings.push(`getBlurhash failed: ${e}`);
return undefined; return undefined;
}); });
@ -114,11 +135,14 @@ export class FileInfoService {
opts.sensitiveThreshold ?? 0.5, opts.sensitiveThreshold ?? 0.5,
opts.sensitiveThresholdForPorn ?? 0.75, opts.sensitiveThresholdForPorn ?? 0.75,
opts.enableSensitiveMediaDetectionForVideos ?? false, opts.enableSensitiveMediaDetectionForVideos ?? false,
).then(value => { ).then(
[sensitive, porn] = value; (value) => {
}, error => { [sensitive, porn] = value;
warnings.push(`detectSensitivity failed: ${error}`); },
}); (error) => {
warnings.push(`detectSensitivity failed: ${error}`);
},
);
} }
return { return {
@ -135,71 +159,100 @@ export class FileInfoService {
}; };
} }
private async detectSensitivity(source: string, mime: string, sensitiveThreshold: number, sensitiveThresholdForPorn: number, analyzeVideo: boolean): Promise<[sensitive: boolean, porn: boolean]> { private async detectSensitivity(
source: string,
mime: string,
sensitiveThreshold: number,
sensitiveThresholdForPorn: number,
analyzeVideo: boolean,
): Promise<[sensitive: boolean, porn: boolean]> {
let sensitive = false; let sensitive = false;
let porn = false; let porn = false;
function judgePrediction(result: readonly predictionType[]): [sensitive: boolean, porn: boolean] { function judgePrediction(
result: readonly predictionType[],
): [sensitive: boolean, porn: boolean] {
let sensitive = false; let sensitive = false;
let porn = false; let porn = false;
if ((result.find(x => x.className === 'Sexy')?.probability ?? 0) > sensitiveThreshold) sensitive = true; if (
if ((result.find(x => x.className === 'Hentai')?.probability ?? 0) > sensitiveThreshold) sensitive = true; (result.find((x) => x.className === "Sexy")?.probability ?? 0) >
if ((result.find(x => x.className === 'Porn')?.probability ?? 0) > sensitiveThreshold) sensitive = true; sensitiveThreshold
)
if ((result.find(x => x.className === 'Porn')?.probability ?? 0) > sensitiveThresholdForPorn) porn = true; sensitive = true;
if (
(result.find((x) => x.className === "Hentai")?.probability ?? 0) >
sensitiveThreshold
)
sensitive = true;
if (
(result.find((x) => x.className === "Porn")?.probability ?? 0) >
sensitiveThreshold
)
sensitive = true;
if (
(result.find((x) => x.className === "Porn")?.probability ?? 0) >
sensitiveThresholdForPorn
)
porn = true;
return [sensitive, porn]; return [sensitive, porn];
} }
if (['image/jpeg', 'image/png', 'image/webp'].includes(mime)) { if (["image/jpeg", "image/png", "image/webp"].includes(mime)) {
const result = await this.aiService.detectSensitive(source); // const result = await this.aiService.detectSensitive(source);
if (result) { // if (result) {
[sensitive, porn] = judgePrediction(result); // [sensitive, porn] = judgePrediction(result);
} // }
} else if (analyzeVideo && (mime === 'image/apng' || mime.startsWith('video/'))) { } else if (
analyzeVideo &&
(mime === "image/apng" || mime.startsWith("video/"))
) {
const [outDir, disposeOutDir] = await createTempDir(); const [outDir, disposeOutDir] = await createTempDir();
try { try {
const command = FFmpeg() const command = FFmpeg()
.input(source) .input(source)
.inputOptions([ .inputOptions([
'-skip_frame', 'nokey', // 可能ならキーフレームのみを取得してほしいとする(そうなるとは限らない) "-skip_frame",
'-lowres', '3', // 元の画質でデコードする必要はないので 1/8 画質でデコードしてもよいとする(そうなるとは限らない) "nokey", // 可能ならキーフレームのみを取得してほしいとする(そうなるとは限らない)
"-lowres",
"3", // 元の画質でデコードする必要はないので 1/8 画質でデコードしてもよいとする(そうなるとは限らない)
]) ])
.noAudio() .noAudio()
.videoFilters([ .videoFilters([
{ {
filter: 'select', // フレームのフィルタリング filter: "select", // フレームのフィルタリング
options: { options: {
e: 'eq(pict_type,PICT_TYPE_I)', // I-Frame のみをフィルタするVP9 とかはデコードしてみないとわからないっぽい) e: "eq(pict_type,PICT_TYPE_I)", // I-Frame のみをフィルタするVP9 とかはデコードしてみないとわからないっぽい)
}, },
}, },
{ {
filter: 'blackframe', // 暗いフレームの検出 filter: "blackframe", // 暗いフレームの検出
options: { options: {
amount: '0', // 暗さに関わらず全てのフレームで測定値を取る amount: "0", // 暗さに関わらず全てのフレームで測定値を取る
}, },
}, },
{ {
filter: 'metadata', filter: "metadata",
options: { options: {
mode: 'select', // フレーム選択モード mode: "select", // フレーム選択モード
key: 'lavfi.blackframe.pblack', // フレームにおける暗部の百分率(前のフィルタからのメタデータを参照する) key: "lavfi.blackframe.pblack", // フレームにおける暗部の百分率(前のフィルタからのメタデータを参照する)
value: '50', value: "50",
function: 'less', // 50% 未満のフレームを選択する50% 以上暗部があるフレームだと誤検知を招くかもしれないので) function: "less", // 50% 未満のフレームを選択する50% 以上暗部があるフレームだと誤検知を招くかもしれないので)
}, },
}, },
{ {
filter: 'scale', filter: "scale",
options: { options: {
w: 299, w: 299,
h: 299, h: 299,
}, },
}, },
]) ])
.format('image2') .format("image2")
.output(join(outDir, '%d.png')) .output(join(outDir, "%d.png"))
.outputOptions(['-vsync', '0']); // 可変フレームレートにすることで穴埋めをさせない .outputOptions(["-vsync", "0"]); // 可変フレームレートにすることで穴埋めをさせない
const results: ReturnType<typeof judgePrediction>[] = []; const results: ReturnType<typeof judgePrediction>[] = [];
let frameIndex = 0; let frameIndex = 0;
let targetIndex = 0; let targetIndex = 0;
@ -212,53 +265,63 @@ export class FileInfoService {
} }
targetIndex = nextIndex; targetIndex = nextIndex;
nextIndex += index; // fibonacci sequence によってフレーム数制限を掛ける nextIndex += index; // fibonacci sequence によってフレーム数制限を掛ける
const result = await this.aiService.detectSensitive(path); // const result = await this.aiService.detectSensitive(path);
if (result) { // if (result) {
results.push(judgePrediction(result)); // results.push(judgePrediction(result));
} // }
} finally { } finally {
fs.promises.unlink(path); fs.promises.unlink(path);
} }
} }
sensitive = results.filter(x => x[0]).length >= Math.ceil(results.length * sensitiveThreshold); sensitive =
porn = results.filter(x => x[1]).length >= Math.ceil(results.length * sensitiveThresholdForPorn); results.filter((x) => x[0]).length >=
Math.ceil(results.length * sensitiveThreshold);
porn =
results.filter((x) => x[1]).length >=
Math.ceil(results.length * sensitiveThresholdForPorn);
} finally { } finally {
disposeOutDir(); disposeOutDir();
} }
} }
return [sensitive, porn]; return [sensitive, porn];
} }
private async *asyncIterateFrames(cwd: string, command: FFmpeg.FfmpegCommand): AsyncGenerator<string, void> { private async *asyncIterateFrames(
cwd: string,
command: FFmpeg.FfmpegCommand,
): AsyncGenerator<string, void> {
const watcher = new FSWatcher({ const watcher = new FSWatcher({
cwd, cwd,
disableGlobbing: true, disableGlobbing: true,
}); });
let finished = false; let finished = false;
command.once('end', () => { command.once("end", () => {
finished = true; finished = true;
watcher.close(); watcher.close();
}); });
command.run(); command.run();
for (let i = 1; true; i++) { // eslint-disable-line @typescript-eslint/no-unnecessary-condition for (let i = 1; true; i++) {
// eslint-disable-line @typescript-eslint/no-unnecessary-condition
const current = `${i}.png`; const current = `${i}.png`;
const next = `${i + 1}.png`; const next = `${i + 1}.png`;
const framePath = join(cwd, current); const framePath = join(cwd, current);
if (await this.exists(join(cwd, next))) { if (await this.exists(join(cwd, next))) {
yield framePath; yield framePath;
} else if (!finished) { // eslint-disable-line @typescript-eslint/no-unnecessary-condition } else if (!finished) {
// eslint-disable-line @typescript-eslint/no-unnecessary-condition
watcher.add(next); watcher.add(next);
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
watcher.on('add', function onAdd(path) { watcher.on("add", function onAdd(path) {
if (path === next) { // 次フレームの書き出しが始まっているなら、現在フレームの書き出しは終わっている if (path === next) {
// 次フレームの書き出しが始まっているなら、現在フレームの書き出しは終わっている
watcher.unwatch(current); watcher.unwatch(current);
watcher.off('add', onAdd); watcher.off("add", onAdd);
resolve(); resolve();
} }
}); });
command.once('end', resolve); // 全てのフレームを処理し終わったなら、最終フレームである現在フレームの書き出しは終わっている command.once("end", resolve); // 全てのフレームを処理し終わったなら、最終フレームである現在フレームの書き出しは終わっている
command.once('error', reject); command.once("error", reject);
}); });
yield framePath; yield framePath;
} else if (await this.exists(framePath)) { } else if (await this.exists(framePath)) {
@ -268,19 +331,22 @@ export class FileInfoService {
} }
} }
} }
private exists(path: string): Promise<boolean> { private exists(path: string): Promise<boolean> {
return fs.promises.access(path).then(() => true, () => false); return fs.promises.access(path).then(
() => true,
() => false,
);
} }
/** /**
* Detect MIME Type and extension * Detect MIME Type and extension
*/ */
public async detectType(path: string): Promise<{ public async detectType(path: string): Promise<{
mime: string; mime: string;
ext: string | null; ext: string | null;
}> { }> {
// Check 0 byte // Check 0 byte
const fileSize = await this.getFileSize(path); const fileSize = await this.getFileSize(path);
if (fileSize === 0) { if (fileSize === 0) {
return TYPE_OCTET_STREAM; return TYPE_OCTET_STREAM;
@ -289,8 +355,8 @@ export class FileInfoService {
const type = await fileTypeFromFile(path); const type = await fileTypeFromFile(path);
if (type) { if (type) {
// XMLはSVGかもしれない // XMLはSVGかもしれない
if (type.mime === 'application/xml' && await this.checkSvg(path)) { if (type.mime === "application/xml" && (await this.checkSvg(path))) {
return TYPE_SVG; return TYPE_SVG;
} }
@ -334,7 +400,7 @@ export class FileInfoService {
* Calculate MD5 hash * Calculate MD5 hash
*/ */
private async calcHash(path: string): Promise<string> { private async calcHash(path: string): Promise<string> {
const hash = crypto.createHash('md5').setEncoding('hex'); const hash = crypto.createHash("md5").setEncoding("hex");
await pipeline(fs.createReadStream(path), hash); await pipeline(fs.createReadStream(path), hash);
return hash.read(); return hash.read();
} }
@ -343,12 +409,12 @@ export class FileInfoService {
* Detect dimensions of image * Detect dimensions of image
*/ */
private async detectImageSize(path: string): Promise<{ private async detectImageSize(path: string): Promise<{
width: number; width: number;
height: number; height: number;
wUnits: string; wUnits: string;
hUnits: string; hUnits: string;
orientation?: number; orientation?: number;
}> { }> {
const readable = fs.createReadStream(path); const readable = fs.createReadStream(path);
const imageSize = await probeImageSize(readable); const imageSize = await probeImageSize(readable);
readable.destroy(); readable.destroy();
@ -363,7 +429,7 @@ export class FileInfoService {
sharp(path) sharp(path)
.raw() .raw()
.ensureAlpha() .ensureAlpha()
.resize(64, 64, { fit: 'inside' }) .resize(64, 64, { fit: "inside" })
.toBuffer((err, buffer, { width, height }) => { .toBuffer((err, buffer, { width, height }) => {
if (err) return reject(err); if (err) return reject(err);

File diff suppressed because it is too large Load Diff

View File

@ -1,87 +1,98 @@
module.exports = { module.exports = {
root: true, root: true,
env: { env: {
'node': false, node: false,
}, },
parser: 'vue-eslint-parser', parser: "vue-eslint-parser",
parserOptions: { parserOptions: {
'parser': '@typescript-eslint/parser', parser: "@typescript-eslint/parser",
tsconfigRootDir: __dirname, tsconfigRootDir: __dirname,
project: ['./tsconfig.json'], project: ["./tsconfig.json"],
extraFileExtensions: ['.vue'], extraFileExtensions: [".vue"],
}, },
extends: [ extends: ["../shared/.eslintrc.js", "plugin:vue/vue3-recommended"],
'../shared/.eslintrc.js',
'plugin:vue/vue3-recommended',
],
rules: { rules: {
'@typescript-eslint/no-empty-interface': [ "@typescript-eslint/no-empty-interface": [
'error', "error",
{ {
'allowSingleExtends': true, allowSingleExtends: true,
}, },
], ],
'@typescript-eslint/prefer-nullish-coalescing': [ "@typescript-eslint/prefer-nullish-coalescing": ["error"],
'error',
],
// window の禁止理由: グローバルスコープと衝突し、予期せぬ結果を招くため // window の禁止理由: グローバルスコープと衝突し、予期せぬ結果を招くため
// e の禁止理由: error や event など、複数のキーワードの頭文字であり分かりにくいため // e の禁止理由: error や event など、複数のキーワードの頭文字であり分かりにくいため
'id-denylist': ['error', 'window', 'e'], "id-denylist": ["error", "window", "e"],
'no-shadow': ['warn'], "no-shadow": ["warn"],
'vue/attributes-order': ['error', { "vue/attributes-order": [
'alphabetical': false, "error",
}], {
'vue/no-use-v-if-with-v-for': ['error', { alphabetical: false,
'allowUsingIterationVar': false, },
}], ],
'vue/no-ref-as-operand': 'error', "vue/no-use-v-if-with-v-for": [
'vue/no-multi-spaces': ['error', { "error",
'ignoreProperties': false, {
}], allowUsingIterationVar: false,
'vue/no-v-html': 'warn', },
'vue/order-in-components': 'error', ],
'vue/html-indent': ['warn', 'tab', { "vue/no-ref-as-operand": "error",
'attribute': 1, "vue/no-multi-spaces": [
'baseIndent': 0, "error",
'closeBracket': 0, {
'alignAttributesVertically': true, ignoreProperties: false,
'ignores': [], },
}], ],
'vue/html-closing-bracket-spacing': ['warn', { "vue/no-v-html": "warn",
'startTag': 'never', "vue/order-in-components": "error",
'endTag': 'never', "vue/html-indent": [
'selfClosingTag': 'never', "warn",
}], "tab",
'vue/multi-word-component-names': 'warn', {
'vue/require-v-for-key': 'warn', attribute: 1,
'vue/no-unused-components': 'warn', baseIndent: 0,
'vue/valid-v-for': 'warn', closeBracket: 0,
'vue/return-in-computed-property': 'warn', alignAttributesVertically: true,
'vue/no-setup-props-destructure': 'warn', ignores: [],
'vue/max-attributes-per-line': 'off', },
'vue/html-self-closing': 'off', ],
'vue/singleline-html-element-content-newline': 'off', "vue/html-closing-bracket-spacing": [
"warn",
{
startTag: "never",
endTag: "never",
selfClosingTag: "never",
},
],
"vue/multi-word-component-names": "warn",
"vue/require-v-for-key": "warn",
"vue/no-unused-components": "warn",
"vue/valid-v-for": "warn",
"vue/return-in-computed-property": "warn",
"vue/no-setup-props-destructure": "warn",
"vue/max-attributes-per-line": "off",
"vue/html-self-closing": "off",
"vue/singleline-html-element-content-newline": "off",
}, },
globals: { globals: {
// Node.js // Node.js
'module': false, module: false,
'require': false, require: false,
'__dirname': false, __dirname: false,
// Vue // Vue
'$$': false, // '$$': false,
'$ref': false, // '$ref': false,
'$shallowRef': false, // '$shallowRef': false,
'$computed': false, // '$computed': false,
// Misskey // Misskey
'_DEV_': false, _DEV_: false,
'_LANGS_': false, _LANGS_: false,
'_VERSION_': false, _VERSION_: false,
'_ENV_': false, _ENV_: false,
'_PERF_PREFIX_': false, _PERF_PREFIX_: false,
'_DATA_TRANSFER_DRIVE_FILE_': false, _DATA_TRANSFER_DRIVE_FILE_: false,
'_DATA_TRANSFER_DRIVE_FOLDER_': false, _DATA_TRANSFER_DRIVE_FOLDER_: false,
'_DATA_TRANSFER_DECK_COLUMN_': false, _DATA_TRANSFER_DECK_COLUMN_: false,
}, },
}; };

View File

@ -3,88 +3,92 @@
"scripts": { "scripts": {
"watch": "vite build --watch --mode development", "watch": "vite build --watch --mode development",
"build": "vite build", "build": "vite build",
"lint": "eslint --quiet \"src/**/*.{ts,vue}\"" "lint": "eslint --quiet \"src/**/*.{ts,vue}\"",
"typecheck:vue": "vue-tsc --noEmit",
"typecheck:tsc": "tsc -p . --noEmit"
}, },
"resolutions": { "resolutions": {
"chokidar": "^3.3.1", "chokidar": "^3.3.1",
"lodash": "^4.17.21" "lodash": "^4.17.21"
}, },
"dependencies": { "dependencies": {
"@discordapp/twemoji": "14.0.2", "@discordapp/twemoji": "15.1.0",
"@fortawesome/fontawesome-free": "6.1.2", "@fortawesome/fontawesome-free": "6.6.0",
"@rollup/plugin-alias": "3.1.9", "@rollup/plugin-alias": "5.1.1",
"@rollup/plugin-json": "4.1.0", "@rollup/plugin-json": "6.1.0",
"@syuilo/aiscript": "0.11.1", "@syuilo/aiscript": "0.11.1",
"@vitejs/plugin-vue": "3.1.0", "@vitejs/plugin-vue": "5.1.4",
"@vue/compiler-sfc": "3.2.39", "@vue/compiler-sfc": "3.5.12",
"autobind-decorator": "2.4.0", "autobind-decorator": "2.4.0",
"autosize": "5.0.1", "autosize": "6.0.1",
"blurhash": "1.1.5", "blurhash": "2.0.5",
"broadcast-channel": "4.17.0", "broadcast-channel": "7.0.0",
"browser-image-resizer": "git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.2", "browser-image-resizer": "git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.2",
"chart.js": "3.9.1", "chart.js": "4.4.4",
"chartjs-adapter-date-fns": "2.0.0", "chartjs-adapter-date-fns": "3.0.0",
"chartjs-plugin-gradient": "0.5.1", "chartjs-plugin-gradient": "0.6.1",
"chartjs-plugin-zoom": "1.2.1", "chartjs-plugin-zoom": "2.0.1",
"compare-versions": "5.0.1", "chokidar": "^4.0.1",
"compare-versions": "6.1.1",
"cropperjs": "2.0.0-beta", "cropperjs": "2.0.0-beta",
"date-fns": "2.29.3", "date-fns": "4.1.0",
"escape-regexp": "0.0.1", "escape-regexp": "0.0.1",
"eventemitter3": "4.0.7", "eventemitter3": "5.0.1",
"idb-keyval": "6.2.0", "idb-keyval": "6.2.1",
"insert-text-at-cursor": "0.3.0", "insert-text-at-cursor": "0.3.0",
"json5": "2.2.1", "json5": "2.2.3",
"katex": "0.15.6", "katex": "0.16.11",
"matter-js": "0.18.0", "matter-js": "0.20.0",
"mfm-js": "git+https://github.com/sim1222/mfm.js.git", "mfm-js": "git+https://github.com/sim1222/mfm.js.git",
"misetehoshii": "https://github.com/melt-adzuki/misetehoshii", "misetehoshii": "https://github.com/melt-adzuki/misetehoshii",
"misskey-js": "0.0.14", "misskey-js": "0.0.14",
"photoswipe": "5.3.2", "photoswipe": "5.4.4",
"prismjs": "1.29.0", "prismjs": "1.29.0",
"punycode": "2.1.1", "punycode": "2.3.1",
"querystring": "0.2.1", "querystring": "0.2.1",
"rndstr": "1.0.0", "rndstr": "1.0.0",
"s-age": "1.1.2", "s-age": "1.1.2",
"sass": "1.54.9", "sass": "1.79.5",
"seedrandom": "3.0.5", "seedrandom": "3.0.5",
"strict-event-emitter-types": "2.0.0", "strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0", "stringz": "2.1.0",
"syuilo-password-strength": "0.0.1", "syuilo-password-strength": "0.0.1",
"textarea-caret": "3.1.0", "textarea-caret": "3.1.0",
"three": "0.144.0", "three": "0.169.0",
"throttle-debounce": "5.0.0", "throttle-debounce": "5.0.2",
"tinycolor2": "1.4.2", "tinycolor2": "1.6.0",
"tsc-alias": "1.7.0", "tsc-alias": "1.8.10",
"tsconfig-paths": "4.1.0", "tsconfig-paths": "4.2.0",
"twemoji-parser": "14.0.0", "twemoji-parser": "14.0.0",
"typescript": "4.8.3", "typescript": "5.6.3",
"uuid": "9.0.0", "uuid": "10.0.0",
"vanilla-tilt": "1.7.2", "vanilla-tilt": "1.8.1",
"vite": "3.1.3", "vite": "5.4.8",
"vue": "3.2.39", "vue": "3.5.12",
"vue-prism-editor": "2.0.0-alpha.2", "vue-prism-editor": "2.0.0-alpha.2",
"vuedraggable": "4.0.1" "vuedraggable": "4.0.1"
}, },
"devDependencies": { "devDependencies": {
"@types/escape-regexp": "0.0.1", "@types/escape-regexp": "0.0.3",
"@types/glob": "8.0.0", "@types/glob": "8.1.0",
"@types/gulp": "4.0.9", "@types/gulp": "4.0.17",
"@types/gulp-rename": "2.0.1", "@types/gulp-rename": "2.0.6",
"@types/katex": "0.14.0", "@types/katex": "0.16.7",
"@types/matter-js": "0.18.2", "@types/matter-js": "0.19.7",
"@types/punycode": "2.1.0", "@types/punycode": "2.1.4",
"@types/seedrandom": "3.0.2", "@types/seedrandom": "3.0.8",
"@types/throttle-debounce": "5.0.0", "@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.3", "@types/tinycolor2": "1.4.6",
"@types/uuid": "8.3.4", "@types/uuid": "10.0.0",
"@typescript-eslint/eslint-plugin": "5.38.0", "@typescript-eslint/eslint-plugin": "8.8.1",
"@typescript-eslint/parser": "5.38.0", "@typescript-eslint/parser": "8.8.1",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cypress": "10.8.0", "cypress": "13.15.0",
"eslint": "8.23.1", "eslint": "9.12.0",
"eslint-plugin-import": "2.26.0", "eslint-plugin-import": "2.31.0",
"eslint-plugin-vue": "9.5.1", "eslint-plugin-vue": "9.29.0",
"rollup": "2.79.0", "rollup": "4.24.0",
"start-server-and-test": "1.14.0" "start-server-and-test": "2.0.8",
"vue-tsc": "2.1.6"
} }
} }

View File

@ -36,6 +36,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkSwitch from '@/components/form/switch.vue'; import MkSwitch from '@/components/form/switch.vue';
import MkKeyValue from '@/components/MkKeyValue.vue'; import MkKeyValue from '@/components/MkKeyValue.vue';
@ -51,11 +53,11 @@ const emit = defineEmits<{
(ev: 'resolved', reportId: string): void; (ev: 'resolved', reportId: string): void;
}>(); }>();
let forward = $ref(props.report.forwarded); let forward = ref(props.report.forwarded);
function resolve() { function resolve() {
os.apiWithDialog('admin/resolve-abuse-user-report', { os.apiWithDialog('admin/resolve-abuse-user-report', {
forward: forward, forward: forward.value,
reportId: props.report.id, reportId: props.report.id,
}).then(() => { }).then(() => {
emit('resolved', props.report.id); emit('resolved', props.report.id);

View File

@ -130,44 +130,44 @@ const texts = computed(() => {
}); });
let enabled = true; let enabled = true;
let majorGraduationColor = $ref<string>(); let majorGraduationColor = ref<string>();
//let minorGraduationColor = $ref<string>(); //let minorGraduationColor = $ref<string>();
let sHandColor = $ref<string>(); let sHandColor = ref<string>();
let mHandColor = $ref<string>(); let mHandColor = ref<string>();
let hHandColor = $ref<string>(); let hHandColor = ref<string>();
let nowColor = $ref<string>(); let nowColor = ref<string>();
let h = $ref<number>(0); let h = ref<number>(0);
let m = $ref<number>(0); let m = ref<number>(0);
let s = $ref<number>(0); let s = ref<number>(0);
let hAngle = $ref<number>(0); let hAngle = ref<number>(0);
let mAngle = $ref<number>(0); let mAngle = ref<number>(0);
let sAngle = $ref<number>(0); let sAngle = ref<number>(0);
let disableSAnimate = $ref(false); let disableSAnimate = ref(false);
let sOneRound = false; let sOneRound = false;
function tick() { function tick() {
const now = new Date(); const now = new Date();
now.setMinutes(now.getMinutes() + (new Date().getTimezoneOffset() + props.offset)); now.setMinutes(now.getMinutes() + (new Date().getTimezoneOffset() + props.offset));
s = now.getSeconds(); s.value = now.getSeconds();
m = now.getMinutes(); m.value = now.getMinutes();
h = now.getHours(); h.value = now.getHours();
hAngle = Math.PI * (h % (props.twentyfour ? 24 : 12) + (m + s / 60) / 60) / (props.twentyfour ? 12 : 6); hAngle.value = Math.PI * (h.value % (props.twentyfour ? 24 : 12) + (m.value + s.value / 60) / 60) / (props.twentyfour ? 12 : 6);
mAngle = Math.PI * (m + s / 60) / 30; mAngle.value = Math.PI * (m.value + s.value / 60) / 30;
if (sOneRound) { // (59->0) if (sOneRound) { // (59->0)
sAngle = Math.PI * 60 / 30; sAngle.value = Math.PI * 60 / 30;
window.setTimeout(() => { window.setTimeout(() => {
disableSAnimate = true; disableSAnimate.value = true;
window.setTimeout(() => { window.setTimeout(() => {
sAngle = 0; sAngle.value = 0;
window.setTimeout(() => { window.setTimeout(() => {
disableSAnimate = false; disableSAnimate.value = false;
}, 100); }, 100);
}, 100); }, 100);
}, 700); }, 700);
} else { } else {
sAngle = Math.PI * s / 30; sAngle.value = Math.PI * s.value / 30;
} }
sOneRound = s === 59; sOneRound = s.value === 59;
} }
tick(); tick();
@ -176,12 +176,12 @@ function calcColors() {
const computedStyle = getComputedStyle(document.documentElement); const computedStyle = getComputedStyle(document.documentElement);
const dark = tinycolor(computedStyle.getPropertyValue('--bg')).isDark(); const dark = tinycolor(computedStyle.getPropertyValue('--bg')).isDark();
const accent = tinycolor(computedStyle.getPropertyValue('--accent')).toHexString(); const accent = tinycolor(computedStyle.getPropertyValue('--accent')).toHexString();
majorGraduationColor = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)'; majorGraduationColor.value = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)';
//minorGraduationColor = dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; //minorGraduationColor = dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
sHandColor = dark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)'; sHandColor.value = dark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)';
mHandColor = tinycolor(computedStyle.getPropertyValue('--fg')).toHexString(); mHandColor.value = tinycolor(computedStyle.getPropertyValue('--fg')).toHexString();
hHandColor = accent; hHandColor.value = accent;
nowColor = accent; nowColor.value = accent;
} }
calcColors(); calcColors();

View File

@ -25,7 +25,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted } from 'vue'; import { nextTick, onMounted, ref } from 'vue';
const props = defineProps<{ const props = defineProps<{
type?: 'button' | 'submit' | 'reset'; type?: 'button' | 'submit' | 'reset';
@ -45,13 +45,13 @@ const emit = defineEmits<{
(ev: 'click', payload: MouseEvent): void; (ev: 'click', payload: MouseEvent): void;
}>(); }>();
let el = $ref<HTMLElement | null>(null); let el = ref<HTMLElement | null>(null);
let ripples = $ref<HTMLElement | null>(null); let ripples = ref<HTMLElement | null>(null);
onMounted(() => { onMounted(() => {
if (props.autofocus) { if (props.autofocus) {
nextTick(() => { nextTick(() => {
el!.focus(); el.value!.focus();
}); });
} }
}); });
@ -77,7 +77,7 @@ function onMousedown(evt: MouseEvent): void {
ripple.style.top = (evt.clientY - rect.top - 1).toString() + 'px'; ripple.style.top = (evt.clientY - rect.top - 1).toString() + 'px';
ripple.style.left = (evt.clientX - rect.left - 1).toString() + 'px'; ripple.style.left = (evt.clientX - rect.left - 1).toString() + 'px';
ripples!.appendChild(ripple); ripples.value!.appendChild(ripple);
const circleCenterX = evt.clientX - rect.left; const circleCenterX = evt.clientX - rect.left;
const circleCenterY = evt.clientY - rect.top; const circleCenterY = evt.clientY - rect.top;
@ -92,7 +92,7 @@ function onMousedown(evt: MouseEvent): void {
ripple.style.opacity = '0'; ripple.style.opacity = '0';
}, 1000); }, 1000);
window.setTimeout(() => { window.setTimeout(() => {
if (ripples) ripples.removeChild(ripple); if (ripples.value) ripples.value.removeChild(ripple);
}, 2000); }, 2000);
} }
</script> </script>

View File

@ -7,7 +7,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onBeforeUnmount } from 'vue'; import { onMounted, onBeforeUnmount, ref } from 'vue';
import MkMenu from './MkMenu.vue'; import MkMenu from './MkMenu.vue';
import { MenuItem } from './types/menu.vue'; import { MenuItem } from './types/menu.vue';
import contains from '@/scripts/contains'; import contains from '@/scripts/contains';
@ -22,16 +22,16 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
let rootEl = $ref<HTMLDivElement>(); let rootEl = ref<HTMLDivElement>();
let zIndex = $ref<number>(os.claimZIndex('high')); let zIndex = ref<number>(os.claimZIndex('high'));
onMounted(() => { onMounted(() => {
let left = props.ev.pageX + 1; // + 1 let left = props.ev.pageX + 1; // + 1
let top = props.ev.pageY + 1; // + 1 let top = props.ev.pageY + 1; // + 1
const width = rootEl.offsetWidth; const width = rootEl.value.offsetWidth;
const height = rootEl.offsetHeight; const height = rootEl.value.offsetHeight;
if (left + width - window.pageXOffset > window.innerWidth) { if (left + width - window.pageXOffset > window.innerWidth) {
left = window.innerWidth - width + window.pageXOffset; left = window.innerWidth - width + window.pageXOffset;
@ -49,8 +49,8 @@ onMounted(() => {
left = 0; left = 0;
} }
rootEl.style.top = `${top}px`; rootEl.value.style.top = `${top}px`;
rootEl.style.left = `${left}px`; rootEl.value.style.left = `${left}px`;
for (const el of Array.from(document.querySelectorAll('body *'))) { for (const el of Array.from(document.querySelectorAll('body *'))) {
el.addEventListener('mousedown', onMousedown); el.addEventListener('mousedown', onMousedown);
@ -64,7 +64,7 @@ onBeforeUnmount(() => {
}); });
function onMousedown(evt: Event) { function onMousedown(evt: Event) {
if (!contains(rootEl, evt.target) && (rootEl !== evt.target)) emit('closed'); if (!contains(rootEl.value, evt.target) && (rootEl.value !== evt.target)) emit('closed');
} }
</script> </script>

View File

@ -26,7 +26,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted } from 'vue'; import { nextTick, onMounted, ref } from 'vue';
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import Cropper from 'cropperjs'; import Cropper from 'cropperjs';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
@ -52,10 +52,10 @@ const props = defineProps<{
const imgUrl = `${url}/proxy/image.webp?${query({ const imgUrl = `${url}/proxy/image.webp?${query({
url: props.file.url, url: props.file.url,
})}`; })}`;
let dialogEl = $ref<InstanceType<typeof XModalWindow>>(); let dialogEl = ref<InstanceType<typeof XModalWindow>>();
let imgEl = $ref<HTMLImageElement>(); let imgEl = ref<HTMLImageElement>();
let cropper: Cropper | null = null; let cropper: Cropper | null = null;
let loading = $ref(true); let loading = ref(true);
const ok = async () => { const ok = async () => {
const promise = new Promise<misskey.entities.DriveFile>(async (res) => { const promise = new Promise<misskey.entities.DriveFile>(async (res) => {
@ -84,16 +84,16 @@ const ok = async () => {
const f = await promise; const f = await promise;
emit('ok', f); emit('ok', f);
dialogEl.close(); dialogEl.value.close();
}; };
const cancel = () => { const cancel = () => {
emit('cancel'); emit('cancel');
dialogEl.close(); dialogEl.value.close();
}; };
const onImageLoad = () => { const onImageLoad = () => {
loading = false; loading.value = false;
if (cropper) { if (cropper) {
cropper.getCropperImage()!.$center('contain'); cropper.getCropperImage()!.$center('contain');
@ -102,7 +102,7 @@ const onImageLoad = () => {
}; };
onMounted(() => { onMounted(() => {
cropper = new Cropper(imgEl, { cropper = new Cropper(imgEl.value, {
}); });
const computedStyle = getComputedStyle(document.documentElement); const computedStyle = getComputedStyle(document.documentElement);

View File

@ -28,7 +28,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onBeforeUnmount, onMounted } from 'vue'; import { onBeforeUnmount, onMounted, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import * as os from '@/os'; import * as os from '@/os';
import { stream } from '@/stream'; import { stream } from '@/stream';
@ -43,9 +43,9 @@ const props = withDefaults(defineProps<{
large: false, large: false,
}); });
let isFollowing = $ref(props.user.isFollowing); let isFollowing = ref(props.user.isFollowing);
let hasPendingFollowRequestFromYou = $ref(props.user.hasPendingFollowRequestFromYou); let hasPendingFollowRequestFromYou = ref(props.user.hasPendingFollowRequestFromYou);
let wait = $ref(false); let wait = ref(false);
const connection = stream.useChannel('main'); const connection = stream.useChannel('main');
if (props.user.isFollowing == null) { if (props.user.isFollowing == null) {
@ -57,16 +57,16 @@ if (props.user.isFollowing == null) {
function onFollowChange(user: Misskey.entities.UserDetailed) { function onFollowChange(user: Misskey.entities.UserDetailed) {
if (user.id === props.user.id) { if (user.id === props.user.id) {
isFollowing = user.isFollowing; isFollowing.value = user.isFollowing;
hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou; hasPendingFollowRequestFromYou.value = user.hasPendingFollowRequestFromYou;
} }
} }
async function onClick() { async function onClick() {
wait = true; wait.value = true;
try { try {
if (isFollowing) { if (isFollowing.value) {
const { canceled } = await os.confirm({ const { canceled } = await os.confirm({
type: 'warning', type: 'warning',
text: i18n.t('unfollowConfirm', { name: props.user.name || props.user.username }), text: i18n.t('unfollowConfirm', { name: props.user.name || props.user.username }),
@ -78,22 +78,22 @@ async function onClick() {
userId: props.user.id userId: props.user.id
}); });
} else { } else {
if (hasPendingFollowRequestFromYou) { if (hasPendingFollowRequestFromYou.value) {
await os.api('following/requests/cancel', { await os.api('following/requests/cancel', {
userId: props.user.id userId: props.user.id
}); });
hasPendingFollowRequestFromYou = false; hasPendingFollowRequestFromYou.value = false;
} else { } else {
await os.api('following/create', { await os.api('following/create', {
userId: props.user.id userId: props.user.id
}); });
hasPendingFollowRequestFromYou = true; hasPendingFollowRequestFromYou.value = true;
} }
} }
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} finally { } finally {
wait = false; wait.value = false;
} }
} }

View File

@ -32,7 +32,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import XModalWindow from '@/components/MkModalWindow.vue'; import XModalWindow from '@/components/MkModalWindow.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
@ -45,20 +45,20 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
let dialog: InstanceType<typeof XModalWindow> = $ref(); let dialog: InstanceType<typeof XModalWindow> = ref();
let username = $ref(''); let username = ref('');
let email = $ref(''); let email = ref('');
let processing = $ref(false); let processing = ref(false);
async function onSubmit() { async function onSubmit() {
processing = true; processing.value = true;
await os.apiWithDialog('request-reset-password', { await os.apiWithDialog('request-reset-password', {
username, username: username.value,
email, email: email.value,
}); });
emit('done'); emit('done');
dialog.close(); dialog.value.close();
} }
</script> </script>

View File

@ -13,7 +13,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import bytes from '@/filters/bytes'; import bytes from '@/filters/bytes';
import number from '@/filters/number'; import number from '@/filters/number';
@ -28,7 +28,7 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const modal = $ref<InstanceType<typeof MkModal>>(); const modal = ref<InstanceType<typeof MkModal>>();
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -6,7 +6,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted, ref } from 'vue';
import { decode } from 'blurhash'; import { decode } from 'blurhash';
import { defaultStore } from '@/store'; import { defaultStore } from '@/store';
@ -30,20 +30,20 @@ const imgSrc = defaultStore.state.mediaProxy ?
: :
props.src; props.src;
const canvas = $ref<HTMLCanvasElement>(); const canvas = ref<HTMLCanvasElement>();
let loaded = $ref(false); let loaded = ref(false);
function draw() { function draw() {
if (props.hash == null) return; if (props.hash == null) return;
const pixels = decode(props.hash, props.size, props.size); const pixels = decode(props.hash, props.size, props.size);
const ctx = canvas.getContext('2d'); const ctx = canvas.value.getContext('2d');
const imageData = ctx!.createImageData(props.size, props.size); const imageData = ctx!.createImageData(props.size, props.size);
imageData.data.set(pixels); imageData.data.set(pixels);
ctx!.putImageData(imageData, 0, 0); ctx!.putImageData(imageData, 0, 0);
} }
function onLoad() { function onLoad() {
loaded = true; loaded.value = true;
} }
onMounted(() => { onMounted(() => {

View File

@ -10,6 +10,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import MkMiniChart from '@/components/MkMiniChart.vue'; import MkMiniChart from '@/components/MkMiniChart.vue';
import * as os from '@/os'; import * as os from '@/os';
@ -18,12 +20,12 @@ const props = defineProps<{
instance: misskey.entities.Instance; instance: misskey.entities.Instance;
}>(); }>();
let chartValues = $ref<number[] | null>(null); let chartValues = ref<number[] | null>(null);
os.apiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => { os.apiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => {
// //
res.requests.received.splice(0, 1); res.requests.received.splice(0, 1);
chartValues = res.requests.received; chartValues.value = res.requests.received;
}); });
</script> </script>

View File

@ -48,7 +48,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted, ref } from 'vue';
import { import {
Chart, Chart,
ArcElement, ArcElement,
@ -98,10 +98,10 @@ const props = withDefaults(defineProps<{
chartLimit: 90, chartLimit: 90,
}); });
const chartSpan = $ref<'hour' | 'day'>('hour'); const chartSpan = ref<'hour' | 'day'>('hour');
const chartSrc = $ref('active-users'); const chartSrc = ref('active-users');
let subDoughnutEl = $ref<HTMLCanvasElement>(); let subDoughnutEl = ref<HTMLCanvasElement>();
let pubDoughnutEl = $ref<HTMLCanvasElement>(); let pubDoughnutEl = ref<HTMLCanvasElement>();
const { handler: externalTooltipHandler1 } = useChartTooltip(); const { handler: externalTooltipHandler1 } = useChartTooltip();
const { handler: externalTooltipHandler2 } = useChartTooltip(); const { handler: externalTooltipHandler2 } = useChartTooltip();
@ -156,7 +156,7 @@ function createDoughnut(chartEl, tooltip, data) {
onMounted(() => { onMounted(() => {
os.apiGet('federation/stats', { limit: 30 }).then(fedStats => { os.apiGet('federation/stats', { limit: 30 }).then(fedStats => {
createDoughnut(subDoughnutEl, externalTooltipHandler1, fedStats.topSubInstances.map(x => ({ createDoughnut(subDoughnutEl.value, externalTooltipHandler1, fedStats.topSubInstances.map(x => ({
name: x.host, name: x.host,
color: x.themeColor, color: x.themeColor,
value: x.followersCount, value: x.followersCount,
@ -165,7 +165,7 @@ onMounted(() => {
}, },
})).concat([{ name: '(other)', color: '#80808080', value: fedStats.otherFollowersCount }])); })).concat([{ name: '(other)', color: '#80808080', value: fedStats.otherFollowersCount }]));
createDoughnut(pubDoughnutEl, externalTooltipHandler2, fedStats.topPubInstances.map(x => ({ createDoughnut(pubDoughnutEl.value, externalTooltipHandler2, fedStats.topPubInstances.map(x => ({
name: x.host, name: x.host,
color: x.themeColor, color: x.themeColor,
value: x.followingCount, value: x.followingCount,

View File

@ -20,7 +20,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import MkModal from '@/components/MkModal.vue'; import MkModal from '@/components/MkModal.vue';
import { navbarItemDef } from '@/navbar'; import { navbarItemDef } from '@/navbar';
import { instanceName } from '@/config'; import { instanceName } from '@/config';
@ -44,7 +44,7 @@ const preferedModalType = (deviceKind === 'desktop' && props.src != null) ? 'pop
deviceKind === 'smartphone' ? 'drawer' : deviceKind === 'smartphone' ? 'drawer' :
'dialog'; 'dialog';
const modal = $ref<InstanceType<typeof MkModal>>(); const modal = ref<InstanceType<typeof MkModal>>();
const menu = defaultStore.state.menu; const menu = defaultStore.state.menu;
@ -58,7 +58,7 @@ const items = Object.keys(navbarItemDef).filter(k => !menu.includes(k)).map(k =>
})); }));
function close() { function close() {
modal.close(); modal.value.close();
} }
</script> </script>

View File

@ -8,7 +8,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent, ref } from 'vue';
import { url as local } from '@/config'; import { url as local } from '@/config';
import { useTooltip } from '@/scripts/use-tooltip'; import { useTooltip } from '@/scripts/use-tooltip';
import * as os from '@/os'; import * as os from '@/os';
@ -23,13 +23,13 @@ const self = props.url.startsWith(local);
const attr = self ? 'to' : 'href'; const attr = self ? 'to' : 'href';
const target = self ? null : '_blank'; const target = self ? null : '_blank';
const el = $ref(); const el = ref();
useTooltip($$(el), (showing) => { useTooltip((el), (showing) => {
os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), { os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
showing, showing,
url: props.url, url: props.url,
source: el, source: el.value,
}, {}, 'closed'); }, {}, 'closed');
}); });
</script> </script>

View File

@ -26,7 +26,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted, ref } from 'vue';
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import { ColdDeviceStorage } from '@/store'; import { ColdDeviceStorage } from '@/store';
@ -35,16 +35,16 @@ const props = withDefaults(defineProps<{
}>(), { }>(), {
}); });
const audioEl = $ref<HTMLAudioElement | null>(); const audioEl = ref<HTMLAudioElement | null>();
let hide = $ref(true); let hide = ref(true);
function volumechange() { function volumechange() {
if (audioEl) ColdDeviceStorage.set('mediaVolume', audioEl.volume); if (audioEl.value) ColdDeviceStorage.set('mediaVolume', audioEl.value.volume);
} }
onMounted(() => { onMounted(() => {
// if (audioEl) audioEl.volume = ColdDeviceStorage.get('mediaVolume'); // if (audioEl) audioEl.volume = ColdDeviceStorage.get('mediaVolume');
if (audioEl) audioEl.volume = 0.2; if (audioEl.value) audioEl.value.volume = 0.2;
}); });
</script> </script>

View File

@ -21,7 +21,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { watch } from 'vue'; import { watch, ref } from 'vue';
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import { getStaticImageUrl } from '@/scripts/get-static-image-url'; import { getStaticImageUrl } from '@/scripts/get-static-image-url';
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue'; import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
@ -32,7 +32,7 @@ const props = defineProps<{
raw?: boolean; raw?: boolean;
}>(); }>();
let hide = $ref(true); let hide = ref(true);
const url = (props.raw || defaultStore.state.loadRawImages) const url = (props.raw || defaultStore.state.loadRawImages)
? props.image.url ? props.image.url
@ -42,7 +42,7 @@ const url = (props.raw || defaultStore.state.loadRawImages)
// Plugin:register_note_view_interruptor 使watch // Plugin:register_note_view_interruptor 使watch
watch(() => props.image, () => { watch(() => props.image, () => {
hide = (defaultStore.state.nsfw === 'force') ? true : props.image.isSensitive && (defaultStore.state.nsfw !== 'ignore'); hide.value = (defaultStore.state.nsfw === 'force') ? true : props.image.isSensitive && (defaultStore.state.nsfw !== 'ignore');
}, { }, {
deep: true, deep: true,
immediate: true, immediate: true,

View File

@ -32,11 +32,11 @@ const props = defineProps<{
video: misskey.entities.DriveFile; video: misskey.entities.DriveFile;
}>(); }>();
const videoEl = $ref<HTMLVideoElement | null>(); const videoEl = ref<HTMLVideoElement | null>();
const hide = ref((defaultStore.state.nsfw === 'force') ? true : props.video.isSensitive && (defaultStore.state.nsfw !== 'ignore')); const hide = ref((defaultStore.state.nsfw === 'force') ? true : props.video.isSensitive && (defaultStore.state.nsfw !== 'ignore'));
onMounted(() => { onMounted(() => {
if (videoEl) videoEl.volume = 0.2; if (videoEl.value) videoEl.value.volume = 0.2;
}); });
</script> </script>

View File

@ -56,7 +56,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue'; import { defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch, computed } from 'vue';
import { focusPrev, focusNext } from '@/scripts/focus'; import { focusPrev, focusNext } from '@/scripts/focus';
import FormSwitch from '@/components/form/switch.vue'; import FormSwitch from '@/components/form/switch.vue';
import { MenuItem, InnerMenuItem, MenuPending, MenuAction } from '@/types/menu'; import { MenuItem, InnerMenuItem, MenuPending, MenuAction } from '@/types/menu';
@ -78,19 +78,19 @@ const emit = defineEmits<{
(ev: 'close', actioned?: boolean): void; (ev: 'close', actioned?: boolean): void;
}>(); }>();
let itemsEl = $ref<HTMLDivElement>(); let itemsEl = ref<HTMLDivElement>();
let items2: InnerMenuItem[] = $ref([]); let items2: InnerMenuItem[] = ref([]);
let child = $ref<InstanceType<typeof XChild>>(); let child = ref<InstanceType<typeof XChild>>();
let keymap = $computed(() => ({ let keymap = computed(() => ({
'up|k|shift+tab': focusUp, 'up|k|shift+tab': focusUp,
'down|j|tab': focusDown, 'down|j|tab': focusDown,
'esc': close, 'esc': close,
})); }));
let childShowingItem = $ref<MenuItem | null>(); let childShowingItem = ref<MenuItem | null>();
watch(() => props.items, () => { watch(() => props.items, () => {
const items: (MenuItem | MenuPending)[] = [...props.items].filter(item => item !== undefined); const items: (MenuItem | MenuPending)[] = [...props.items].filter(item => item !== undefined);
@ -101,22 +101,22 @@ watch(() => props.items, () => {
if (item && 'then' in item) { // if item is Promise if (item && 'then' in item) { // if item is Promise
items[i] = { type: 'pending' }; items[i] = { type: 'pending' };
item.then(actualItem => { item.then(actualItem => {
items2[i] = actualItem; items2.value[i] = actualItem;
}); });
} }
} }
items2 = items as InnerMenuItem[]; items2.value = items as InnerMenuItem[];
}, { }, {
immediate: true, immediate: true,
}); });
let childMenu = $ref<MenuItem[] | null>(); let childMenu = ref<MenuItem[] | null>();
let childTarget = $ref<HTMLElement | null>(); let childTarget = ref<HTMLElement | null>();
function closeChild() { function closeChild() {
childMenu = null; childMenu.value = null;
childShowingItem = null; childShowingItem.value = null;
} }
function childActioned() { function childActioned() {
@ -125,8 +125,8 @@ function childActioned() {
} }
function onGlobalMousedown(event: MouseEvent) { function onGlobalMousedown(event: MouseEvent) {
if (childTarget && (event.target === childTarget || childTarget.contains(event.target))) return; if (childTarget.value && (event.target === childTarget.value || childTarget.value.contains(event.target))) return;
if (child && child.checkHit(event)) return; if (child.value && child.value.checkHit(event)) return;
closeChild(); closeChild();
} }
@ -145,9 +145,9 @@ async function showChildren(item: MenuItem, ev: MouseEvent) {
os.popupMenu(item.children, ev.currentTarget ?? ev.target); os.popupMenu(item.children, ev.currentTarget ?? ev.target);
close(); close();
} else { } else {
childTarget = ev.currentTarget ?? ev.target; childTarget.value = ev.currentTarget ?? ev.target;
childMenu = item.children; childMenu.value = item.children;
childShowingItem = item; childShowingItem.value = item;
} }
} }
@ -171,7 +171,7 @@ function focusDown() {
onMounted(() => { onMounted(() => {
if (props.viaKeyboard) { if (props.viaKeyboard) {
nextTick(() => { nextTick(() => {
focusNext(itemsEl.children[0], true, false); focusNext(itemsEl.value.children[0], true, false);
}); });
} }

View File

@ -26,7 +26,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onUnmounted, watch } from 'vue'; import { onUnmounted, watch, ref } from 'vue';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
import { useInterval } from '@/scripts/use-interval'; import { useInterval } from '@/scripts/use-interval';
@ -38,11 +38,11 @@ const props = defineProps<{
const viewBoxX = 50; const viewBoxX = 50;
const viewBoxY = 50; const viewBoxY = 50;
const gradientId = uuid(); const gradientId = uuid();
let polylinePoints = $ref(''); let polylinePoints = ref('');
let polygonPoints = $ref(''); let polygonPoints = ref('');
let headX = $ref<number | null>(null); let headX = ref<number | null>(null);
let headY = $ref<number | null>(null); let headY = ref<number | null>(null);
let clock = $ref<number | null>(null); let clock = ref<number | null>(null);
const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent')); const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent'));
const color = accent.toRgbString(); const color = accent.toRgbString();
@ -55,12 +55,12 @@ function draw(): void {
(1 - (n / peak)) * viewBoxY, (1 - (n / peak)) * viewBoxY,
]); ]);
polylinePoints = _polylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' '); polylinePoints.value = _polylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
polygonPoints = `0,${ viewBoxY } ${ polylinePoints } ${ viewBoxX },${ viewBoxY }`; polygonPoints.value = `0,${ viewBoxY } ${ polylinePoints.value } ${ viewBoxX },${ viewBoxY }`;
headX = _polylinePoints[_polylinePoints.length - 1][0]; headX.value = _polylinePoints[_polylinePoints.length - 1][0];
headY = _polylinePoints[_polylinePoints.length - 1][1]; headY.value = _polylinePoints[_polylinePoints.length - 1][1];
} }
watch(() => props.src, draw, { immediate: true }); watch(() => props.src, draw, { immediate: true });

View File

@ -10,7 +10,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, watch, provide } from 'vue'; import { nextTick, onMounted, watch, provide, ref, computed } from 'vue';
import * as os from '@/os'; import * as os from '@/os';
import { isTouchUsing } from '@/scripts/touch'; import { isTouchUsing } from '@/scripts/touch';
import { defaultStore } from '@/store'; import { defaultStore } from '@/store';
@ -57,13 +57,13 @@ const emit = defineEmits<{
provide('modal', true); provide('modal', true);
let maxHeight = $ref<number>(); let maxHeight = ref<number>();
let fixed = $ref(false); let fixed = ref(false);
let transformOrigin = $ref('center'); let transformOrigin = ref('center');
let showing = $ref(true); let showing = ref(true);
let content = $ref<HTMLElement>(); let content = ref<HTMLElement>();
const zIndex = os.claimZIndex(props.zPriority); const zIndex = os.claimZIndex(props.zPriority);
const type = $computed(() => { const type = computed(() => {
if (props.preferType === 'auto') { if (props.preferType === 'auto') {
if (!defaultStore.state.disableDrawer && isTouchUsing && deviceKind === 'smartphone') { if (!defaultStore.state.disableDrawer && isTouchUsing && deviceKind === 'smartphone') {
return 'drawer'; return 'drawer';
@ -80,7 +80,7 @@ let contentClicking = false;
const close = () => { const close = () => {
// eslint-disable-next-line vue/no-mutating-props // eslint-disable-next-line vue/no-mutating-props
if (props.src) props.src.style.pointerEvents = 'auto'; if (props.src) props.src.style.pointerEvents = 'auto';
showing = false; showing.value = false;
emit('close'); emit('close');
}; };
@ -89,8 +89,8 @@ const onBgClick = () => {
emit('click'); emit('click');
}; };
if (type === 'drawer') { if (type.value === 'drawer') {
maxHeight = window.innerHeight / 1.5; maxHeight.value = window.innerHeight / 1.5;
} }
const keymap = { const keymap = {
@ -101,21 +101,21 @@ const MARGIN = 16;
const align = () => { const align = () => {
if (props.src == null) return; if (props.src == null) return;
if (type === 'drawer') return; if (type.value === 'drawer') return;
if (type === 'dialog') return; if (type.value === 'dialog') return;
if (content == null) return; if (content.value == null) return;
const srcRect = props.src.getBoundingClientRect(); const srcRect = props.src.getBoundingClientRect();
const width = content!.offsetWidth; const width = content.value!.offsetWidth;
const height = content!.offsetHeight; const height = content.value!.offsetHeight;
let left; let left;
let top; let top;
const x = srcRect.left + (fixed ? 0 : window.pageXOffset); const x = srcRect.left + (fixed.value ? 0 : window.pageXOffset);
const y = srcRect.top + (fixed ? 0 : window.pageYOffset); const y = srcRect.top + (fixed.value ? 0 : window.pageYOffset);
if (props.anchor.x === 'center') { if (props.anchor.x === 'center') {
left = x + (props.src.offsetWidth / 2) - (width / 2); left = x + (props.src.offsetWidth / 2) - (width / 2);
@ -133,7 +133,7 @@ const align = () => {
top = y + props.src.offsetHeight; top = y + props.src.offsetHeight;
} }
if (fixed) { if (fixed.value) {
// //
if (left + width > window.innerWidth) { if (left + width > window.innerWidth) {
left = window.innerWidth - width; left = window.innerWidth - width;
@ -146,16 +146,16 @@ const align = () => {
if (top + height > (window.innerHeight - MARGIN)) { if (top + height > (window.innerHeight - MARGIN)) {
if (props.noOverlap && props.anchor.x === 'center') { if (props.noOverlap && props.anchor.x === 'center') {
if (underSpace >= (upperSpace / 3)) { if (underSpace >= (upperSpace / 3)) {
maxHeight = underSpace; maxHeight.value = underSpace;
} else { } else {
maxHeight = upperSpace; maxHeight.value = upperSpace;
top = (upperSpace + MARGIN) - height; top = (upperSpace + MARGIN) - height;
} }
} else { } else {
top = (window.innerHeight - MARGIN) - height; top = (window.innerHeight - MARGIN) - height;
} }
} else { } else {
maxHeight = underSpace; maxHeight.value = underSpace;
} }
} else { } else {
// //
@ -170,16 +170,16 @@ const align = () => {
if (top + height - window.pageYOffset > (window.innerHeight - MARGIN)) { if (top + height - window.pageYOffset > (window.innerHeight - MARGIN)) {
if (props.noOverlap && props.anchor.x === 'center') { if (props.noOverlap && props.anchor.x === 'center') {
if (underSpace >= (upperSpace / 3)) { if (underSpace >= (upperSpace / 3)) {
maxHeight = underSpace; maxHeight.value = underSpace;
} else { } else {
maxHeight = upperSpace; maxHeight.value = upperSpace;
top = window.pageYOffset + ((upperSpace + MARGIN) - height); top = window.pageYOffset + ((upperSpace + MARGIN) - height);
} }
} else { } else {
top = (window.innerHeight - MARGIN) - height + window.pageYOffset - 1; top = (window.innerHeight - MARGIN) - height + window.pageYOffset - 1;
} }
} else { } else {
maxHeight = underSpace; maxHeight.value = underSpace;
} }
} }
@ -194,29 +194,29 @@ const align = () => {
let transformOriginX = 'center'; let transformOriginX = 'center';
let transformOriginY = 'center'; let transformOriginY = 'center';
if (top >= srcRect.top + props.src.offsetHeight + (fixed ? 0 : window.pageYOffset)) { if (top >= srcRect.top + props.src.offsetHeight + (fixed.value ? 0 : window.pageYOffset)) {
transformOriginY = 'top'; transformOriginY = 'top';
} else if ((top + height) <= srcRect.top + (fixed ? 0 : window.pageYOffset)) { } else if ((top + height) <= srcRect.top + (fixed.value ? 0 : window.pageYOffset)) {
transformOriginY = 'bottom'; transformOriginY = 'bottom';
} }
if (left >= srcRect.left + props.src.offsetWidth + (fixed ? 0 : window.pageXOffset)) { if (left >= srcRect.left + props.src.offsetWidth + (fixed.value ? 0 : window.pageXOffset)) {
transformOriginX = 'left'; transformOriginX = 'left';
} else if ((left + width) <= srcRect.left + (fixed ? 0 : window.pageXOffset)) { } else if ((left + width) <= srcRect.left + (fixed.value ? 0 : window.pageXOffset)) {
transformOriginX = 'right'; transformOriginX = 'right';
} }
transformOrigin = `${transformOriginX} ${transformOriginY}`; transformOrigin.value = `${transformOriginX} ${transformOriginY}`;
content.style.left = left + 'px'; content.value.style.left = left + 'px';
content.style.top = top + 'px'; content.value.style.top = top + 'px';
}; };
const onOpened = () => { const onOpened = () => {
emit('opened'); emit('opened');
// //
const el = content!.children[0]; const el = content.value!.children[0];
el.addEventListener('mousedown', ev => { el.addEventListener('mousedown', ev => {
contentClicking = true; contentClicking = true;
window.addEventListener('mouseup', ev => { window.addEventListener('mouseup', ev => {
@ -234,7 +234,7 @@ onMounted(() => {
// eslint-disable-next-line vue/no-mutating-props // eslint-disable-next-line vue/no-mutating-props
props.src.style.pointerEvents = 'none'; props.src.style.pointerEvents = 'none';
} }
fixed = (type === 'drawer') || (getFixedContainer(props.src) != null); fixed.value = (type.value === 'drawer') || (getFixedContainer(props.src) != null);
await nextTick(); await nextTick();
@ -244,7 +244,7 @@ onMounted(() => {
nextTick(() => { nextTick(() => {
new ResizeObserver((entries, observer) => { new ResizeObserver((entries, observer) => {
align(); align();
}).observe(content!); }).observe(content.value!);
}); });
}); });

View File

@ -21,7 +21,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ComputedRef, provide } from 'vue'; import { ComputedRef, provide, ref, computed } from 'vue';
import MkModal from '@/components/MkModal.vue'; import MkModal from '@/components/MkModal.vue';
import { popout as _popout } from '@/scripts/popout'; import { popout as _popout } from '@/scripts/popout';
import copyToClipboard from '@/scripts/copy-to-clipboard'; import copyToClipboard from '@/scripts/copy-to-clipboard';
@ -47,26 +47,26 @@ router.addListener('push', ctx => {
}); });
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>(); let pageMetadata = ref<null | ComputedRef<PageMetadata>>();
let rootEl = $ref(); let rootEl = ref();
let modal = $ref<InstanceType<typeof MkModal>>(); let modal = ref<InstanceType<typeof MkModal>>();
let path = $ref(props.initialPath); let path = ref(props.initialPath);
let width = $ref(860); let width = ref(860);
let height = $ref(660); let height = ref(660);
const history = []; const history = [];
provide('router', router); provide('router', router);
provideMetadataReceiver((info) => { provideMetadataReceiver((info) => {
pageMetadata = info; pageMetadata.value = info;
}); });
provide('shouldOmitHeaderTitle', true); provide('shouldOmitHeaderTitle', true);
provide('shouldHeaderThin', true); provide('shouldHeaderThin', true);
const pageUrl = $computed(() => url + path); const pageUrl = computed(() => url + path.value);
const contextmenu = $computed(() => { const contextmenu = computed(() => {
return [{ return [{
type: 'label', type: 'label',
text: path, text: path.value,
}, { }, {
icon: 'fas fa-expand-alt', icon: 'fas fa-expand-alt',
text: i18n.ts.showInPage, text: i18n.ts.showInPage,
@ -79,14 +79,14 @@ const contextmenu = $computed(() => {
icon: 'fas fa-external-link-alt', icon: 'fas fa-external-link-alt',
text: i18n.ts.openInNewTab, text: i18n.ts.openInNewTab,
action: () => { action: () => {
window.open(pageUrl, '_blank'); window.open(pageUrl.value, '_blank');
modal.close(); modal.value.close();
}, },
}, { }, {
icon: 'fas fa-link', icon: 'fas fa-link',
text: i18n.ts.copyLink, text: i18n.ts.copyLink,
action: () => { action: () => {
copyToClipboard(pageUrl); copyToClipboard(pageUrl.value);
}, },
}]; }];
}); });
@ -101,17 +101,17 @@ function back() {
} }
function expand() { function expand() {
mainRouter.push(path); mainRouter.push(path.value);
modal.close(); modal.value.close();
} }
function popout() { function popout() {
_popout(path, rootEl); _popout(path.value, rootEl.value);
modal.close(); modal.value.close();
} }
function onContextmenu(ev: MouseEvent) { function onContextmenu(ev: MouseEvent) {
os.contextMenu(contextmenu, ev); os.contextMenu(contextmenu.value, ev);
} }
</script> </script>

View File

@ -17,7 +17,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted } from 'vue'; import { onMounted, onUnmounted, ref } from 'vue';
import MkModal from './MkModal.vue'; import MkModal from './MkModal.vue';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
@ -41,14 +41,14 @@ const emit = defineEmits<{
(event: 'ok'): void; (event: 'ok'): void;
}>(); }>();
let modal = $ref<InstanceType<typeof MkModal>>(); let modal = ref<InstanceType<typeof MkModal>>();
let rootEl = $ref<HTMLElement>(); let rootEl = ref<HTMLElement>();
let headerEl = $ref<HTMLElement>(); let headerEl = ref<HTMLElement>();
let bodyWidth = $ref(0); let bodyWidth = ref(0);
let bodyHeight = $ref(0); let bodyHeight = ref(0);
const close = () => { const close = () => {
modal.close(); modal.value.close();
}; };
const onBgClick = () => { const onBgClick = () => {
@ -64,14 +64,14 @@ const onKeydown = (evt) => {
}; };
const ro = new ResizeObserver((entries, observer) => { const ro = new ResizeObserver((entries, observer) => {
bodyWidth = rootEl.offsetWidth; bodyWidth.value = rootEl.value.offsetWidth;
bodyHeight = rootEl.offsetHeight - headerEl.offsetHeight; bodyHeight.value = rootEl.value.offsetHeight - headerEl.value.offsetHeight;
}); });
onMounted(() => { onMounted(() => {
bodyWidth = rootEl.offsetWidth; bodyWidth.value = rootEl.value.offsetWidth;
bodyHeight = rootEl.offsetHeight - headerEl.offsetHeight; bodyHeight.value = rootEl.value.offsetHeight - headerEl.value.offsetHeight;
ro.observe(rootEl); ro.observe(rootEl.value);
}); });
onUnmounted(() => { onUnmounted(() => {

View File

@ -137,24 +137,24 @@ const props = defineProps<{
const inChannel = inject('inChannel', null); const inChannel = inject('inChannel', null);
let note = $ref(JSON.parse(JSON.stringify(props.note))); let note = ref(JSON.parse(JSON.stringify(props.note)));
// plugin // plugin
if (noteViewInterruptors.length > 0) { if (noteViewInterruptors.length > 0) {
onMounted(async () => { onMounted(async () => {
let result = JSON.parse(JSON.stringify(note)); let result = JSON.parse(JSON.stringify(note.value));
for (const interruptor of noteViewInterruptors) { for (const interruptor of noteViewInterruptors) {
result = await interruptor.handler(result); result = await interruptor.handler(result);
} }
note = result; note.value = result;
}); });
} }
const isRenote = ( const isRenote = (
note.renote != null && note.value.renote != null &&
note.text == null && note.value.text == null &&
note.fileIds.length === 0 && note.value.fileIds.length === 0 &&
note.poll == null note.value.poll == null
); );
const el = ref<HTMLElement>(); const el = ref<HTMLElement>();
@ -162,20 +162,20 @@ const menuButton = ref<HTMLElement>();
const renoteButton = ref<InstanceType<typeof XRenoteButton>>(); const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
const renoteTime = ref<HTMLElement>(); const renoteTime = ref<HTMLElement>();
const reactButton = ref<HTMLElement>(); const reactButton = ref<HTMLElement>();
let appearNote = $computed(() => isRenote ? note.renote as misskey.entities.Note : note); let appearNote = computed(() => isRenote ? note.value.renote as misskey.entities.Note : note.value);
const isMyRenote = $i && ($i.id === note.userId); const isMyRenote = $i && ($i.id === note.value.userId);
const showContent = ref(false); const showContent = ref(false);
const isLong = (appearNote.cw == null && appearNote.text != null && ( const isLong = (appearNote.value.cw == null && appearNote.value.text != null && (
(appearNote.text.split('\n').length > 9) || (appearNote.value.text.split('\n').length > 9) ||
(appearNote.text.length > 500) (appearNote.value.text.length > 500)
)); ));
const collapsed = ref(appearNote.cw == null && isLong); const collapsed = ref(appearNote.value.cw == null && isLong);
const isDeleted = ref(false); const isDeleted = ref(false);
const muted = ref(checkWordMute(appearNote, $i, defaultStore.state.mutedWords)); const muted = ref(checkWordMute(appearNote.value, $i, defaultStore.state.mutedWords));
const translation = ref(null); const translation = ref(null);
const translating = ref(false); const translating = ref(false);
const urls = appearNote.text ? extractUrlFromMfm(mfm.parse(appearNote.text)) : null; const urls = appearNote.value.text ? extractUrlFromMfm(mfm.parse(appearNote.value.text)) : null;
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance); const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance);
const keymap = { const keymap = {
'r': () => reply(true), 'r': () => reply(true),
@ -190,14 +190,14 @@ const keymap = {
useNoteCapture({ useNoteCapture({
rootEl: el, rootEl: el,
note: $$(appearNote), note: (appearNote),
isDeletedRef: isDeleted, isDeletedRef: isDeleted,
}); });
function reply(viaKeyboard = false): void { function reply(viaKeyboard = false): void {
pleaseLogin(); pleaseLogin();
os.post({ os.post({
reply: appearNote, reply: appearNote.value,
animation: !viaKeyboard, animation: !viaKeyboard,
}, () => { }, () => {
focus(); focus();
@ -209,12 +209,12 @@ function react(viaKeyboard = false): void {
blur(); blur();
reactionPicker.show(reactButton.value, results => { reactionPicker.show(reactButton.value, results => {
os.api('notes/reactions/create', { os.api('notes/reactions/create', {
noteId: appearNote.id, noteId: appearNote.value.id,
reaction: results.reaction, reaction: results.reaction,
}); });
if (results.withRenote) { if (results.withRenote) {
os.api('notes/create', { os.api('notes/create', {
renoteId: appearNote.id, renoteId: appearNote.value.id,
isRenote: true, isRenote: true,
}); });
} }
@ -247,12 +247,12 @@ function onContextmenu(ev: MouseEvent): void {
ev.preventDefault(); ev.preventDefault();
react(); react();
} else { } else {
os.contextMenu(getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, currentClipPage }), ev).then(focus); os.contextMenu(getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted, currentClipPage }), ev).then(focus);
} }
} }
function menu(viaKeyboard = false): void { function menu(viaKeyboard = false): void {
os.popupMenu(getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, currentClipPage }), menuButton.value, { os.popupMenu(getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted, currentClipPage }), menuButton.value, {
viaKeyboard, viaKeyboard,
}).then(focus); }).then(focus);
} }
@ -265,7 +265,7 @@ function showRenoteMenu(viaKeyboard = false): void {
danger: true, danger: true,
action: () => { action: () => {
os.api('notes/delete', { os.api('notes/delete', {
noteId: note.id, noteId: note.value.id,
}); });
isDeleted.value = true; isDeleted.value = true;
}, },
@ -292,7 +292,7 @@ function focusAfter() {
function readPromo() { function readPromo() {
os.api('promo/read', { os.api('promo/read', {
noteId: appearNote.id, noteId: appearNote.value.id,
}); });
isDeleted.value = true; isDeleted.value = true;
} }

View File

@ -147,24 +147,24 @@ const props = defineProps<{
const inChannel = inject('inChannel', null); const inChannel = inject('inChannel', null);
let note = $ref(JSON.parse(JSON.stringify(props.note))); let note = ref(JSON.parse(JSON.stringify(props.note)));
// plugin // plugin
if (noteViewInterruptors.length > 0) { if (noteViewInterruptors.length > 0) {
onMounted(async () => { onMounted(async () => {
let result = JSON.parse(JSON.stringify(note)); let result = JSON.parse(JSON.stringify(note.value));
for (const interruptor of noteViewInterruptors) { for (const interruptor of noteViewInterruptors) {
result = await interruptor.handler(result); result = await interruptor.handler(result);
} }
note = result; note.value = result;
}); });
} }
const isRenote = ( const isRenote = (
note.renote != null && note.value.renote != null &&
note.text == null && note.value.text == null &&
note.fileIds.length === 0 && note.value.fileIds.length === 0 &&
note.poll == null note.value.poll == null
); );
const el = ref<HTMLElement>(); const el = ref<HTMLElement>();
@ -172,15 +172,15 @@ const menuButton = ref<HTMLElement>();
const renoteButton = ref<InstanceType<typeof XRenoteButton>>(); const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
const renoteTime = ref<HTMLElement>(); const renoteTime = ref<HTMLElement>();
const reactButton = ref<HTMLElement>(); const reactButton = ref<HTMLElement>();
let appearNote = $computed(() => isRenote ? note.renote as misskey.entities.Note : note); let appearNote = computed(() => isRenote ? note.value.renote as misskey.entities.Note : note.value);
const isMyRenote = $i && ($i.id === note.userId); const isMyRenote = $i && ($i.id === note.value.userId);
const showContent = ref(false); const showContent = ref(false);
const isDeleted = ref(false); const isDeleted = ref(false);
const muted = ref(checkWordMute(appearNote, $i, defaultStore.state.mutedWords)); const muted = ref(checkWordMute(appearNote.value, $i, defaultStore.state.mutedWords));
const translation = ref(null); const translation = ref(null);
const translating = ref(false); const translating = ref(false);
const urls = appearNote.text ? extractUrlFromMfm(mfm.parse(appearNote.text)) : null; const urls = appearNote.value.text ? extractUrlFromMfm(mfm.parse(appearNote.value.text)) : null;
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance); const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance);
const conversation = ref<misskey.entities.Note[]>([]); const conversation = ref<misskey.entities.Note[]>([]);
const replies = ref<misskey.entities.Note[]>([]); const replies = ref<misskey.entities.Note[]>([]);
@ -195,14 +195,14 @@ const keymap = {
useNoteCapture({ useNoteCapture({
rootEl: el, rootEl: el,
note: $$(appearNote), note: (appearNote),
isDeletedRef: isDeleted, isDeletedRef: isDeleted,
}); });
function reply(viaKeyboard = false): void { function reply(viaKeyboard = false): void {
pleaseLogin(); pleaseLogin();
os.post({ os.post({
reply: appearNote, reply: appearNote.value,
animation: !viaKeyboard, animation: !viaKeyboard,
}, () => { }, () => {
focus(); focus();
@ -214,7 +214,7 @@ function react(viaKeyboard = false): void {
blur(); blur();
reactionPicker.show(reactButton.value, reaction => { reactionPicker.show(reactButton.value, reaction => {
os.api('notes/reactions/create', { os.api('notes/reactions/create', {
noteId: appearNote.id, noteId: appearNote.value.id,
reaction: reaction.reaction, reaction: reaction.reaction,
}); });
}, () => { }, () => {
@ -244,12 +244,12 @@ function onContextmenu(ev: MouseEvent): void {
ev.preventDefault(); ev.preventDefault();
react(); react();
} else { } else {
os.contextMenu(getNoteMenu({ note: note, translating, translation, menuButton, isDeleted }), ev).then(focus); os.contextMenu(getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted }), ev).then(focus);
} }
} }
function menu(viaKeyboard = false): void { function menu(viaKeyboard = false): void {
os.popupMenu(getNoteMenu({ note: note, translating, translation, menuButton, isDeleted }), menuButton.value, { os.popupMenu(getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted }), menuButton.value, {
viaKeyboard, viaKeyboard,
}).then(focus); }).then(focus);
} }
@ -262,7 +262,7 @@ function showRenoteMenu(viaKeyboard = false): void {
danger: true, danger: true,
action: () => { action: () => {
os.api('notes/delete', { os.api('notes/delete', {
noteId: note.id, noteId: note.value.id,
}); });
isDeleted.value = true; isDeleted.value = true;
}, },
@ -280,15 +280,15 @@ function blur() {
} }
os.api('notes/children', { os.api('notes/children', {
noteId: appearNote.id, noteId: appearNote.value.id,
limit: 30, limit: 30,
}).then(res => { }).then(res => {
replies.value = res; replies.value = res;
}); });
if (appearNote.replyId) { if (appearNote.value.replyId) {
os.api('notes/conversation', { os.api('notes/conversation', {
noteId: appearNote.replyId, noteId: appearNote.value.replyId,
}).then(res => { }).then(res => {
conversation.value = res.reverse(); conversation.value = res.reverse();
}); });

View File

@ -17,7 +17,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import XNoteHeader from '@/components/MkNoteHeader.vue'; import XNoteHeader from '@/components/MkNoteHeader.vue';
import MkSubNoteContent from '@/components/MkSubNoteContent.vue'; import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
@ -28,7 +28,7 @@ const props = defineProps<{
pinned?: boolean; pinned?: boolean;
}>(); }>();
const showContent = $ref(false); const showContent = ref(false);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -25,7 +25,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import XNoteHeader from '@/components/MkNoteHeader.vue'; import XNoteHeader from '@/components/MkNoteHeader.vue';
import MkSubNoteContent from '@/components/MkSubNoteContent.vue'; import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
@ -44,15 +44,15 @@ const props = withDefaults(defineProps<{
depth: 1, depth: 1,
}); });
let showContent = $ref(false); let showContent = ref(false);
let replies: misskey.entities.Note[] = $ref([]); let replies: misskey.entities.Note[] = ref([]);
if (props.detail) { if (props.detail) {
os.api('notes/children', { os.api('notes/children', {
noteId: props.note.id, noteId: props.note.id,
limit: 5, limit: 5,
}).then(res => { }).then(res => {
replies = res; replies.value = res;
}); });
} }
</script> </script>

View File

@ -28,7 +28,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { computed, ref } from 'vue';
import { notificationTypes } from 'misskey-js'; import { notificationTypes } from 'misskey-js';
import MkSwitch from './form/switch.vue'; import MkSwitch from './form/switch.vue';
import MkInfo from './MkInfo.vue'; import MkInfo from './MkInfo.vue';
@ -49,39 +49,39 @@ const props = withDefaults(defineProps<{
showGlobalToggle: true, showGlobalToggle: true,
}); });
let includingTypes = $computed(() => props.includingTypes || []); let includingTypes = computed(() => props.includingTypes || []);
const dialog = $ref<InstanceType<typeof XModalWindow>>(); const dialog = ref<InstanceType<typeof XModalWindow>>();
let typesMap = $ref<Record<typeof notificationTypes[number], boolean>>({}); let typesMap = ref<Record<typeof notificationTypes[number], boolean>>({});
let useGlobalSetting = $ref((includingTypes === null || includingTypes.length === 0) && props.showGlobalToggle); let useGlobalSetting = ref((includingTypes.value === null || includingTypes.value.length === 0) && props.showGlobalToggle);
for (const ntype of notificationTypes) { for (const ntype of notificationTypes) {
typesMap[ntype] = includingTypes.includes(ntype); typesMap.value[ntype] = includingTypes.value.includes(ntype);
} }
function ok() { function ok() {
if (useGlobalSetting) { if (useGlobalSetting.value) {
emit('done', { includingTypes: null }); emit('done', { includingTypes: null });
} else { } else {
emit('done', { emit('done', {
includingTypes: (Object.keys(typesMap) as typeof notificationTypes[number][]) includingTypes: (Object.keys(typesMap.value) as typeof notificationTypes[number][])
.filter(type => typesMap[type]), .filter(type => typesMap.value[type]),
}); });
} }
dialog.close(); dialog.value.close();
} }
function disableAll() { function disableAll() {
for (const type in typesMap) { for (const type in typesMap.value) {
typesMap[type as typeof notificationTypes[number]] = false; typesMap.value[type as typeof notificationTypes[number]] = false;
} }
} }
function enableAll() { function enableAll() {
for (const type in typesMap) { for (const type in typesMap.value) {
typesMap[type as typeof notificationTypes[number]] = true; typesMap.value[type as typeof notificationTypes[number]] = true;
} }
} }
</script> </script>

View File

@ -7,7 +7,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted, ref } from 'vue';
import XNotification from '@/components/MkNotification.vue'; import XNotification from '@/components/MkNotification.vue';
import * as os from '@/os'; import * as os from '@/os';
@ -20,11 +20,11 @@ const emit = defineEmits<{
}>(); }>();
const zIndex = os.claimZIndex('high'); const zIndex = os.claimZIndex('high');
let showing = $ref(true); let showing = ref(true);
onMounted(() => { onMounted(() => {
window.setTimeout(() => { window.setTimeout(() => {
showing = false; showing.value = false;
}, 6000); }, 6000);
}); });
</script> </script>

View File

@ -24,7 +24,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ComputedRef, inject, provide } from 'vue'; import { ComputedRef, inject, provide, ref, computed } from 'vue';
import RouterView from '@/components/global/RouterView.vue'; import RouterView from '@/components/global/RouterView.vue';
import XWindow from '@/components/MkWindow.vue'; import XWindow from '@/components/MkWindow.vue';
import { popout as _popout } from '@/scripts/popout'; import { popout as _popout } from '@/scripts/popout';
@ -46,16 +46,16 @@ defineEmits<{
const router = new Router(routes, props.initialPath); const router = new Router(routes, props.initialPath);
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>(); let pageMetadata = ref<null | ComputedRef<PageMetadata>>();
let windowEl = $ref<InstanceType<typeof XWindow>>(); let windowEl = ref<InstanceType<typeof XWindow>>();
const history = $ref<{ path: string; key: any; }[]>([{ const history = ref<{ path: string; key: any; }[]>([{
path: router.getCurrentPath(), path: router.getCurrentPath(),
key: router.getCurrentKey(), key: router.getCurrentKey(),
}]); }]);
const buttonsLeft = $computed(() => { const buttonsLeft = computed(() => {
const buttons = []; const buttons = [];
if (history.length > 1) { if (history.value.length > 1) {
buttons.push({ buttons.push({
icon: 'fas fa-arrow-left', icon: 'fas fa-arrow-left',
onClick: back, onClick: back,
@ -64,7 +64,7 @@ const buttonsLeft = $computed(() => {
return buttons; return buttons;
}); });
const buttonsRight = $computed(() => { const buttonsRight = computed(() => {
const buttons = [{ const buttons = [{
icon: 'fas fa-expand-alt', icon: 'fas fa-expand-alt',
title: i18n.ts.showInPage, title: i18n.ts.showInPage,
@ -75,17 +75,17 @@ const buttonsRight = $computed(() => {
}); });
router.addListener('push', ctx => { router.addListener('push', ctx => {
history.push({ path: ctx.path, key: ctx.key }); history.value.push({ path: ctx.path, key: ctx.key });
}); });
provide('router', router); provide('router', router);
provideMetadataReceiver((info) => { provideMetadataReceiver((info) => {
pageMetadata = info; pageMetadata.value = info;
}); });
provide('shouldOmitHeaderTitle', true); provide('shouldOmitHeaderTitle', true);
provide('shouldHeaderThin', true); provide('shouldHeaderThin', true);
const contextmenu = $computed(() => ([{ const contextmenu = computed(() => ([{
icon: 'fas fa-expand-alt', icon: 'fas fa-expand-alt',
text: i18n.ts.showInPage, text: i18n.ts.showInPage,
action: expand, action: expand,
@ -98,7 +98,7 @@ const contextmenu = $computed(() => ([{
text: i18n.ts.openInNewTab, text: i18n.ts.openInNewTab,
action: () => { action: () => {
window.open(url + router.getCurrentPath(), '_blank'); window.open(url + router.getCurrentPath(), '_blank');
windowEl.close(); windowEl.value.close();
}, },
}, { }, {
icon: 'fas fa-link', icon: 'fas fa-link',
@ -109,26 +109,26 @@ const contextmenu = $computed(() => ([{
}])); }]));
function menu(ev) { function menu(ev) {
os.popupMenu(contextmenu, ev.currentTarget ?? ev.target); os.popupMenu(contextmenu.value, ev.currentTarget ?? ev.target);
} }
function back() { function back() {
history.pop(); history.value.pop();
router.replace(history[history.length - 1].path, history[history.length - 1].key); router.replace(history.value[history.value.length - 1].path, history.value[history.value.length - 1].key);
} }
function close() { function close() {
windowEl.close(); windowEl.value.close();
} }
function expand() { function expand() {
mainRouter.push(router.getCurrentPath(), 'forcePage'); mainRouter.push(router.getCurrentPath(), 'forcePage');
windowEl.close(); windowEl.value.close();
} }
function popout() { function popout() {
_popout(router.getCurrentPath(), windowEl.$el); _popout(router.getCurrentPath(), windowEl.value.$el);
windowEl.close(); windowEl.value.close();
} }
defineExpose({ defineExpose({

View File

@ -5,7 +5,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import MkModal from './MkModal.vue'; import MkModal from './MkModal.vue';
import MkMenu from './MkMenu.vue'; import MkMenu from './MkMenu.vue';
import { MenuItem } from '@/types/menu'; import { MenuItem } from '@/types/menu';
@ -22,7 +22,7 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
let modal = $ref<InstanceType<typeof MkModal>>(); let modal = ref<InstanceType<typeof MkModal>>();
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -65,7 +65,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject, watch, nextTick, onMounted, defineAsyncComponent } from 'vue'; import { inject, watch, nextTick, onMounted, defineAsyncComponent, ref, computed } from 'vue';
import * as mfm from 'mfm-js'; import * as mfm from 'mfm-js';
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import insertTextAtCursor from 'insert-text-at-cursor'; import insertTextAtCursor from 'insert-text-at-cursor';
@ -121,35 +121,35 @@ const emit = defineEmits<{
(ev: 'esc'): void; (ev: 'esc'): void;
}>(); }>();
const textareaEl = $ref<HTMLTextAreaElement | null>(null); const textareaEl = ref<HTMLTextAreaElement | null>(null);
const cwInputEl = $ref<HTMLInputElement | null>(null); const cwInputEl = ref<HTMLInputElement | null>(null);
const hashtagsInputEl = $ref<HTMLInputElement | null>(null); const hashtagsInputEl = ref<HTMLInputElement | null>(null);
const visibilityButton = $ref<HTMLElement | null>(null); const visibilityButton = ref<HTMLElement | null>(null);
let posting = $ref(false); let posting = ref(false);
let text = $ref(props.initialText ?? ''); let text = ref(props.initialText ?? '');
let files = $ref(props.initialFiles ?? []); let files = ref(props.initialFiles ?? []);
let poll = $ref<{ let poll = ref<{
choices: string[]; choices: string[];
multiple: boolean; multiple: boolean;
expiresAt: string | null; expiresAt: string | null;
expiredAfter: string | null; expiredAfter: string | null;
} | null>(null); } | null>(null);
let useCw = $ref(false); let useCw = ref(false);
let showPreview = $ref(false); let showPreview = ref(false);
let cw = $ref<string | null>(null); let cw = ref<string | null>(null);
let localOnly = $ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly); let localOnly = ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly);
let visibility = $ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof misskey.noteVisibilities[number]); let visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof misskey.noteVisibilities[number]);
let visibleUsers = $ref([]); let visibleUsers = ref([]);
if (props.initialVisibleUsers) { if (props.initialVisibleUsers) {
props.initialVisibleUsers.forEach(pushVisibleUser); props.initialVisibleUsers.forEach(pushVisibleUser);
} }
let autocomplete = $ref(null); let autocomplete = ref(null);
let draghover = $ref(false); let draghover = ref(false);
let quoteId = $ref(null); let quoteId = ref(null);
let hasNotSpecifiedMentions = $ref(false); let hasNotSpecifiedMentions = ref(false);
let recentHashtags = $ref(JSON.parse(localStorage.getItem('hashtags') || '[]')); let recentHashtags = ref(JSON.parse(localStorage.getItem('hashtags') || '[]'));
let imeText = $ref(''); let imeText = ref('');
const typing = throttle(3000, () => { const typing = throttle(3000, () => {
if (props.channel) { if (props.channel) {
@ -157,7 +157,7 @@ const typing = throttle(3000, () => {
} }
}); });
const draftKey = $computed((): string => { const draftKey = computed((): string => {
let key = props.channel ? `channel:${props.channel.id}` : ''; let key = props.channel ? `channel:${props.channel.id}` : '';
if (props.renote) { if (props.renote) {
@ -171,7 +171,7 @@ const draftKey = $computed((): string => {
return key; return key;
}); });
const placeholder = $computed((): string => { const placeholder = computed((): string => {
if (props.renote) { if (props.renote) {
return i18n.ts._postForm.quotePlaceholder; return i18n.ts._postForm.quotePlaceholder;
} else if (props.reply) { } else if (props.reply) {
@ -191,7 +191,7 @@ const placeholder = $computed((): string => {
} }
}); });
const submitText = $computed((): string => { const submitText = computed((): string => {
return props.renote return props.renote
? i18n.ts.quote ? i18n.ts.quote
: props.reply : props.reply
@ -199,41 +199,41 @@ const submitText = $computed((): string => {
: i18n.ts.note; : i18n.ts.note;
}); });
const textLength = $computed((): number => { const textLength = computed((): number => {
return length((text + imeText).trim()); return length((text.value + imeText.value).trim());
}); });
const maxTextLength = $computed((): number => { const maxTextLength = computed((): number => {
return instance ? instance.maxNoteTextLength : 1000; return instance ? instance.maxNoteTextLength : 1000;
}); });
const canPost = $computed((): boolean => { const canPost = computed((): boolean => {
return !posting && return !posting.value &&
(1 <= textLength || 1 <= files.length || !!poll || !!props.renote) && (1 <= textLength.value || 1 <= files.value.length || !!poll.value || !!props.renote) &&
(textLength <= maxTextLength) && (textLength.value <= maxTextLength.value) &&
(!poll || poll.choices.length >= 2); (!poll.value || poll.value.choices.length >= 2);
}); });
const withHashtags = $computed(defaultStore.makeGetterSetter('postFormWithHashtags')); const withHashtags = computed(defaultStore.makeGetterSetter('postFormWithHashtags'));
const hashtags = $computed(defaultStore.makeGetterSetter('postFormHashtags')); const hashtags = computed(defaultStore.makeGetterSetter('postFormHashtags'));
watch($$(text), () => { watch((text), () => {
checkMissingMention(); checkMissingMention();
}); });
watch($$(visibleUsers), () => { watch((visibleUsers), () => {
checkMissingMention(); checkMissingMention();
}, { }, {
deep: true, deep: true,
}); });
if (props.mention) { if (props.mention) {
text = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`; text.value = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`;
text += ' '; text.value += ' ';
} }
if (props.reply && (props.reply.user.username !== $i.username || (props.reply.user.host != null && props.reply.user.host !== host))) { if (props.reply && (props.reply.user.username !== $i.username || (props.reply.user.host != null && props.reply.user.host !== host))) {
text = `@${props.reply.user.username}${props.reply.user.host != null ? '@' + toASCII(props.reply.user.host) : ''} `; text.value = `@${props.reply.user.username}${props.reply.user.host != null ? '@' + toASCII(props.reply.user.host) : ''} `;
} }
if (props.reply && props.reply.text != null) { if (props.reply && props.reply.text != null) {
@ -251,20 +251,20 @@ if (props.reply && props.reply.text != null) {
if ($i.username === x.username && (x.host == null || x.host === host)) continue; if ($i.username === x.username && (x.host == null || x.host === host)) continue;
// //
if (text.includes(`${mention} `)) continue; if (text.value.includes(`${mention} `)) continue;
text += `${mention} `; text.value += `${mention} `;
} }
} }
if (props.channel) { if (props.channel) {
visibility = 'public'; visibility.value = 'public';
localOnly = true; // TODO: localOnly.value = true; // TODO:
} }
// //
if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visibility)) { if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visibility)) {
visibility = props.reply.visibility; visibility.value = props.reply.visibility;
if (props.reply.visibility === 'specified') { if (props.reply.visibility === 'specified') {
os.api('users/show', { os.api('users/show', {
userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply.userId), userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply.userId),
@ -281,57 +281,57 @@ if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visib
} }
if (props.specified) { if (props.specified) {
visibility = 'specified'; visibility.value = 'specified';
pushVisibleUser(props.specified); pushVisibleUser(props.specified);
} }
// keep cw when reply // keep cw when reply
if (defaultStore.state.keepCw && props.reply && props.reply.cw) { if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
useCw = true; useCw.value = true;
cw = props.reply.cw; cw.value = props.reply.cw;
} }
function watchForDraft() { function watchForDraft() {
watch($$(text), () => saveDraft()); watch((text), () => saveDraft());
watch($$(useCw), () => saveDraft()); watch((useCw), () => saveDraft());
watch($$(cw), () => saveDraft()); watch((cw), () => saveDraft());
watch($$(poll), () => saveDraft()); watch((poll), () => saveDraft());
watch($$(files), () => saveDraft(), { deep: true }); watch((files), () => saveDraft(), { deep: true });
watch($$(visibility), () => saveDraft()); watch((visibility), () => saveDraft());
watch($$(localOnly), () => saveDraft()); watch((localOnly), () => saveDraft());
} }
function checkMissingMention() { function checkMissingMention() {
if (visibility === 'specified') { if (visibility.value === 'specified') {
const ast = mfm.parse(text); const ast = mfm.parse(text.value);
for (const x of extractMentions(ast)) { for (const x of extractMentions(ast)) {
if (!visibleUsers.some(u => (u.username === x.username) && (u.host === x.host))) { if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) {
hasNotSpecifiedMentions = true; hasNotSpecifiedMentions.value = true;
return; return;
} }
} }
hasNotSpecifiedMentions = false; hasNotSpecifiedMentions.value = false;
} }
} }
function addMissingMention() { function addMissingMention() {
const ast = mfm.parse(text); const ast = mfm.parse(text.value);
for (const x of extractMentions(ast)) { for (const x of extractMentions(ast)) {
if (!visibleUsers.some(u => (u.username === x.username) && (u.host === x.host))) { if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) {
os.api('users/show', { username: x.username, host: x.host }).then(user => { os.api('users/show', { username: x.username, host: x.host }).then(user => {
visibleUsers.push(user); visibleUsers.value.push(user);
}); });
} }
} }
} }
function togglePoll() { function togglePoll() {
if (poll) { if (poll.value) {
poll = null; poll.value = null;
} else { } else {
poll = { poll.value = {
choices: ['', ''], choices: ['', ''],
multiple: false, multiple: false,
expiresAt: null, expiresAt: null,
@ -341,49 +341,49 @@ function togglePoll() {
} }
function addTag(tag: string) { function addTag(tag: string) {
insertTextAtCursor(textareaEl, ` #${tag} `); insertTextAtCursor(textareaEl.value, ` #${tag} `);
} }
function focus() { function focus() {
if (textareaEl) { if (textareaEl.value) {
textareaEl.focus(); textareaEl.value.focus();
textareaEl.setSelectionRange(textareaEl.value.length, textareaEl.value.length); textareaEl.value.setSelectionRange(textareaEl.value.value.length, textareaEl.value.value.length);
} }
} }
function chooseFileFrom(ev) { function chooseFileFrom(ev) {
selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => { selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => {
for (const file of files_) { for (const file of files_) {
files.push(file); files.value.push(file);
} }
}); });
} }
function chooseFileFromEnc(ev) { function chooseFileFromEnc(ev) {
selectFileEnc(ev.currentTarget ?? ev.target, i18n.ts.attachVideoFile).then(files_ => { selectFileEnc(ev.currentTarget ?? ev.target, i18n.ts.attachVideoFile).then(files_ => {
files.push(files_); files.value.push(files_);
}); });
} }
function detachFile(id) { function detachFile(id) {
files = files.filter(x => x.id !== id); files.value = files.value.filter(x => x.id !== id);
} }
function updateFiles(_files) { function updateFiles(_files) {
files = _files; files.value = _files;
} }
function updateFileSensitive(file, sensitive) { function updateFileSensitive(file, sensitive) {
files[files.findIndex(x => x.id === file.id)].isSensitive = sensitive; files.value[files.value.findIndex(x => x.id === file.id)].isSensitive = sensitive;
} }
function updateFileName(file, name) { function updateFileName(file, name) {
files[files.findIndex(x => x.id === file.id)].name = name; files.value[files.value.findIndex(x => x.id === file.id)].name = name;
} }
function upload(file: File, name?: string) { function upload(file: File, name?: string) {
uploadFile(file, defaultStore.state.uploadFolder, name).then(res => { uploadFile(file, defaultStore.state.uploadFolder, name).then(res => {
files.push(res); files.value.push(res);
}); });
} }
@ -394,28 +394,28 @@ function setVisibility() {
} }
os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), { os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), {
currentVisibility: visibility, currentVisibility: visibility.value,
currentLocalOnly: localOnly, currentLocalOnly: localOnly.value,
src: visibilityButton, src: visibilityButton.value,
}, { }, {
changeVisibility: v => { changeVisibility: v => {
visibility = v; visibility.value = v;
if (defaultStore.state.rememberNoteVisibility) { if (defaultStore.state.rememberNoteVisibility) {
defaultStore.set('visibility', visibility); defaultStore.set('visibility', visibility.value);
} }
}, },
changeLocalOnly: v => { changeLocalOnly: v => {
localOnly = v; localOnly.value = v;
if (defaultStore.state.rememberNoteVisibility) { if (defaultStore.state.rememberNoteVisibility) {
defaultStore.set('localOnly', localOnly); defaultStore.set('localOnly', localOnly.value);
} }
}, },
}, 'closed'); }, 'closed');
} }
function pushVisibleUser(user) { function pushVisibleUser(user) {
if (!visibleUsers.some(u => u.username === user.username && u.host === user.host)) { if (!visibleUsers.value.some(u => u.username === user.username && u.host === user.host)) {
visibleUsers.push(user); visibleUsers.value.push(user);
} }
} }
@ -426,29 +426,29 @@ function addVisibleUser() {
} }
function removeVisibleUser(user) { function removeVisibleUser(user) {
visibleUsers = erase(user, visibleUsers); visibleUsers.value = erase(user, visibleUsers.value);
} }
function clear() { function clear() {
text = ''; text.value = '';
files = []; files.value = [];
poll = null; poll.value = null;
quoteId = null; quoteId.value = null;
} }
function onKeydown(ev: KeyboardEvent) { function onKeydown(ev: KeyboardEvent) {
if ((ev.which === 10 || ev.which === 13) && (ev.ctrlKey || ev.metaKey) && canPost) post(); if ((ev.which === 10 || ev.which === 13) && (ev.ctrlKey || ev.metaKey) && canPost.value) post();
if (ev.which === 27) emit('esc'); if (ev.which === 27) emit('esc');
typing(); typing();
} }
function onCompositionUpdate(ev: CompositionEvent) { function onCompositionUpdate(ev: CompositionEvent) {
imeText = ev.data; imeText.value = ev.data;
typing(); typing();
} }
function onCompositionEnd(ev: CompositionEvent) { function onCompositionEnd(ev: CompositionEvent) {
imeText = ''; imeText.value = '';
} }
async function onPaste(ev: ClipboardEvent) { async function onPaste(ev: ClipboardEvent) {
@ -464,7 +464,7 @@ async function onPaste(ev: ClipboardEvent) {
const paste = ev.clipboardData.getData('text'); const paste = ev.clipboardData.getData('text');
if (!props.renote && !quoteId && paste.startsWith(url + '/notes/')) { if (!props.renote && !quoteId.value && paste.startsWith(url + '/notes/')) {
ev.preventDefault(); ev.preventDefault();
os.confirm({ os.confirm({
@ -472,11 +472,11 @@ async function onPaste(ev: ClipboardEvent) {
text: i18n.ts.quoteQuestion, text: i18n.ts.quoteQuestion,
}).then(({ canceled }) => { }).then(({ canceled }) => {
if (canceled) { if (canceled) {
insertTextAtCursor(textareaEl, paste); insertTextAtCursor(textareaEl.value, paste);
return; return;
} }
quoteId = paste.substr(url.length).match(/^\/notes\/(.+?)\/?$/)[1]; quoteId.value = paste.substr(url.length).match(/^\/notes\/(.+?)\/?$/)[1];
}); });
} }
} }
@ -487,7 +487,7 @@ function onDragover(ev) {
const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_; const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
if (isFile || isDriveFile) { if (isFile || isDriveFile) {
ev.preventDefault(); ev.preventDefault();
draghover = true; draghover.value = true;
switch (ev.dataTransfer.effectAllowed) { switch (ev.dataTransfer.effectAllowed) {
case 'all': case 'all':
case 'uninitialized': case 'uninitialized':
@ -508,15 +508,15 @@ function onDragover(ev) {
} }
function onDragenter(ev) { function onDragenter(ev) {
draghover = true; draghover.value = true;
} }
function onDragleave(ev) { function onDragleave(ev) {
draghover = false; draghover.value = false;
} }
function onDrop(ev): void { function onDrop(ev): void {
draghover = false; draghover.value = false;
// //
if (ev.dataTransfer.files.length > 0) { if (ev.dataTransfer.files.length > 0) {
@ -529,7 +529,7 @@ function onDrop(ev): void {
const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_); const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
if (driveFile != null && driveFile !== '') { if (driveFile != null && driveFile !== '') {
const file = JSON.parse(driveFile); const file = JSON.parse(driveFile);
files.push(file); files.value.push(file);
ev.preventDefault(); ev.preventDefault();
} }
//#endregion //#endregion
@ -538,16 +538,16 @@ function onDrop(ev): void {
function saveDraft() { function saveDraft() {
const draftData = JSON.parse(localStorage.getItem('drafts') || '{}'); const draftData = JSON.parse(localStorage.getItem('drafts') || '{}');
draftData[draftKey] = { draftData[draftKey.value] = {
updatedAt: new Date(), updatedAt: new Date(),
data: { data: {
text: text, text: text.value,
useCw: useCw, useCw: useCw.value,
cw: cw, cw: cw.value,
visibility: visibility, visibility: visibility.value,
localOnly: localOnly, localOnly: localOnly.value,
files: files, files: files.value,
poll: poll, poll: poll.value,
}, },
}; };
@ -557,27 +557,27 @@ function saveDraft() {
function deleteDraft() { function deleteDraft() {
const draftData = JSON.parse(localStorage.getItem('drafts') || '{}'); const draftData = JSON.parse(localStorage.getItem('drafts') || '{}');
delete draftData[draftKey]; delete draftData[draftKey.value];
localStorage.setItem('drafts', JSON.stringify(draftData)); localStorage.setItem('drafts', JSON.stringify(draftData));
} }
async function post() { async function post() {
let postData = { let postData = {
text: text === '' ? undefined : text, text: text.value === '' ? undefined : text.value,
fileIds: files.length > 0 ? files.map(f => f.id) : undefined, fileIds: files.value.length > 0 ? files.value.map(f => f.id) : undefined,
replyId: props.reply ? props.reply.id : undefined, replyId: props.reply ? props.reply.id : undefined,
renoteId: props.renote ? props.renote.id : quoteId ? quoteId : undefined, renoteId: props.renote ? props.renote.id : quoteId.value ? quoteId.value : undefined,
channelId: props.channel ? props.channel.id : undefined, channelId: props.channel ? props.channel.id : undefined,
poll: poll, poll: poll.value,
cw: useCw ? cw || '' : undefined, cw: useCw.value ? cw.value || '' : undefined,
localOnly: localOnly, localOnly: localOnly.value,
visibility: visibility, visibility: visibility.value,
visibleUserIds: visibility === 'specified' ? visibleUsers.map(u => u.id) : undefined, visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined,
}; };
if (withHashtags && hashtags && hashtags.trim() !== '') { if (withHashtags.value && hashtags.value && hashtags.value.trim() !== '') {
const hashtags_ = hashtags.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' '); const hashtags_ = hashtags.value.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' ');
postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_; postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_;
} }
@ -590,12 +590,12 @@ async function post() {
let token = undefined; let token = undefined;
if (postAccount) { if (postAccount.value) {
const storedAccounts = await getAccounts(); const storedAccounts = await getAccounts();
token = storedAccounts.find(x => x.id === postAccount.id)?.token; token = storedAccounts.find(x => x.id === postAccount.value.id)?.token;
} }
posting = true; posting.value = true;
os.api('notes/create', postData, token).then(() => { os.api('notes/create', postData, token).then(() => {
clear(); clear();
nextTick(() => { nextTick(() => {
@ -606,11 +606,11 @@ async function post() {
const history = JSON.parse(localStorage.getItem('hashtags') || '[]') as string[]; const history = JSON.parse(localStorage.getItem('hashtags') || '[]') as string[];
localStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history)))); localStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history))));
} }
posting = false; posting.value = false;
postAccount = null; postAccount.value = null;
}); });
}).catch(err => { }).catch(err => {
posting = false; posting.value = false;
os.alert({ os.alert({
type: 'error', type: 'error',
text: err.message + '\n' + (err as any).id, text: err.message + '\n' + (err as any).id,
@ -624,12 +624,12 @@ function cancel() {
function insertMention() { function insertMention() {
os.selectUser().then(user => { os.selectUser().then(user => {
insertTextAtCursor(textareaEl, '@' + Acct.toString(user) + ' '); insertTextAtCursor(textareaEl.value, '@' + Acct.toString(user) + ' ');
}); });
} }
async function insertEmoji(ev: MouseEvent) { async function insertEmoji(ev: MouseEvent) {
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl); os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl.value);
} }
function showActions(ev) { function showActions(ev) {
@ -637,26 +637,26 @@ function showActions(ev) {
text: action.title, text: action.title,
action: () => { action: () => {
action.handler({ action.handler({
text: text, text: text.value,
}, (key, value) => { }, (key, value) => {
if (key === 'text') { text = value; } if (key === 'text') { text.value = value; }
}); });
}, },
})), ev.currentTarget ?? ev.target); })), ev.currentTarget ?? ev.target);
} }
let postAccount = $ref<misskey.entities.UserDetailed | null>(null); let postAccount = ref<misskey.entities.UserDetailed | null>(null);
function openAccountMenu(ev: MouseEvent) { function openAccountMenu(ev: MouseEvent) {
openAccountMenu_({ openAccountMenu_({
withExtraOperation: false, withExtraOperation: false,
includeCurrentAccount: true, includeCurrentAccount: true,
active: postAccount != null ? postAccount.id : $i.id, active: postAccount.value != null ? postAccount.value.id : $i.id,
onChoose: (account) => { onChoose: (account) => {
if (account.id === $i.id) { if (account.id === $i.id) {
postAccount = null; postAccount.value = null;
} else { } else {
postAccount = account; postAccount.value = account;
} }
}, },
}, ev); }, ev);
@ -672,23 +672,23 @@ onMounted(() => {
} }
// TODO: detach when unmount // TODO: detach when unmount
new Autocomplete(textareaEl, $$(text)); new Autocomplete(textareaEl.value, (text));
new Autocomplete(cwInputEl, $$(cw)); new Autocomplete(cwInputEl.value, (cw));
new Autocomplete(hashtagsInputEl, $$(hashtags)); new Autocomplete(hashtagsInputEl.value, (hashtags));
nextTick(() => { nextTick(() => {
// 稿 // 稿
if (!props.instant && !props.mention && !props.specified) { if (!props.instant && !props.mention && !props.specified) {
const draft = JSON.parse(localStorage.getItem('drafts') || '{}')[draftKey]; const draft = JSON.parse(localStorage.getItem('drafts') || '{}')[draftKey.value];
if (draft) { if (draft) {
text = draft.data.text; text.value = draft.data.text;
useCw = draft.data.useCw; useCw.value = draft.data.useCw;
cw = draft.data.cw; cw.value = draft.data.cw;
visibility = draft.data.visibility; visibility.value = draft.data.visibility;
localOnly = draft.data.localOnly; localOnly.value = draft.data.localOnly;
files = (draft.data.files || []).filter(draftFile => draftFile); files.value = (draft.data.files || []).filter(draftFile => draftFile);
if (draft.data.poll) { if (draft.data.poll) {
poll = draft.data.poll; poll.value = draft.data.poll;
} }
} }
} }
@ -696,21 +696,21 @@ onMounted(() => {
// //
if (props.initialNote) { if (props.initialNote) {
const init = props.initialNote; const init = props.initialNote;
text = init.text ? init.text : ''; text.value = init.text ? init.text : '';
files = init.files; files.value = init.files;
cw = init.cw; cw.value = init.cw;
useCw = init.cw != null; useCw.value = init.cw != null;
if (init.poll) { if (init.poll) {
poll = { poll.value = {
choices: init.poll.choices.map(x => x.text), choices: init.poll.choices.map(x => x.text),
multiple: init.poll.multiple, multiple: init.poll.multiple,
expiresAt: init.poll.expiresAt, expiresAt: init.poll.expiresAt,
expiredAfter: init.poll.expiredAfter, expiredAfter: init.poll.expiredAfter,
}; };
} }
visibility = init.visibility; visibility.value = init.visibility;
localOnly = init.localOnly; localOnly.value = init.localOnly;
quoteId = init.renote ? init.renote.id : null; quoteId.value = init.renote ? init.renote.id : null;
} }
nextTick(() => watchForDraft()); nextTick(() => watchForDraft());

View File

@ -43,7 +43,7 @@ type reactionUsers = {
users: UserLite[]; users: UserLite[];
}[] }[]
const dialog = $ref<InstanceType<typeof MkModalWindow>>(); const dialog = ref<InstanceType<typeof MkModalWindow>>();
const el = ref<HTMLElement>(); const el = ref<HTMLElement>();
const reactions = ref([] as NoteReaction[]); const reactions = ref([] as NoteReaction[]);
const reactUsers = ref([] as reactionUsers); const reactUsers = ref([] as reactionUsers);

View File

@ -49,7 +49,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent, ref, computed } from 'vue';
import { toUnicode } from 'punycode/'; import { toUnicode } from 'punycode/';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
@ -62,20 +62,20 @@ import { showSuspendedDialog } from '../scripts/show-suspended-dialog';
import { instance } from '@/instance'; import { instance } from '@/instance';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
let signing = $ref(false); let signing = ref(false);
let user = $ref(null); let user = ref(null);
let username = $ref(''); let username = ref('');
let password = $ref(''); let password = ref('');
let token = $ref(''); let token = ref('');
let host = $ref(toUnicode(configHost)); let host = ref(toUnicode(configHost));
let totpLogin = $ref(false); let totpLogin = ref(false);
let credential = $ref(null); let credential = ref(null);
let challengeData = $ref(null); let challengeData = ref(null);
let queryingKey = $ref(false); let queryingKey = ref(false);
let hCaptchaResponse = $ref(null); let hCaptchaResponse = ref(null);
let reCaptchaResponse = $ref(null); let reCaptchaResponse = ref(null);
const meta = $computed(() => instance); const meta = computed(() => instance);
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'login', v: any): void; (ev: 'login', v: any): void;
@ -101,11 +101,11 @@ const props = defineProps({
function onUsernameChange() { function onUsernameChange() {
os.api('users/show', { os.api('users/show', {
username: username username: username.value
}).then(userResponse => { }).then(userResponse => {
user = userResponse; user.value = userResponse;
}, () => { }, () => {
user = null; user.value = null;
}); });
} }
@ -116,11 +116,11 @@ function onLogin(res) {
} }
function queryKey() { function queryKey() {
queryingKey = true; queryingKey.value = true;
return navigator.credentials.get({ return navigator.credentials.get({
publicKey: { publicKey: {
challenge: byteify(challengeData.challenge, 'base64'), challenge: byteify(challengeData.value.challenge, 'base64'),
allowCredentials: challengeData.securityKeys.map(key => ({ allowCredentials: challengeData.value.securityKeys.map(key => ({
id: byteify(key.id, 'hex'), id: byteify(key.id, 'hex'),
type: 'public-key', type: 'public-key',
transports: ['usb', 'nfc', 'ble', 'internal'] transports: ['usb', 'nfc', 'ble', 'internal']
@ -128,21 +128,21 @@ function queryKey() {
timeout: 60 * 1000 timeout: 60 * 1000
} }
}).catch(() => { }).catch(() => {
queryingKey = false; queryingKey.value = false;
return Promise.reject(null); return Promise.reject(null);
}).then(credential => { }).then(credential => {
queryingKey = false; queryingKey.value = false;
signing = true; signing.value = true;
return os.api('signin', { return os.api('signin', {
username, username: username.value,
password, password: password.value,
signature: hexify(credential.response.signature), signature: hexify(credential.response.signature),
authenticatorData: hexify(credential.response.authenticatorData), authenticatorData: hexify(credential.response.authenticatorData),
clientDataJSON: hexify(credential.response.clientDataJSON), clientDataJSON: hexify(credential.response.clientDataJSON),
credentialId: credential.id, credentialId: credential.id,
challengeId: challengeData.challengeId, challengeId: challengeData.value.challengeId,
'hcaptcha-response': hCaptchaResponse, 'hcaptcha-response': hCaptchaResponse.value,
'g-recaptcha-response': reCaptchaResponse, 'g-recaptcha-response': reCaptchaResponse.value,
}); });
}).then(res => { }).then(res => {
emit('login', res); emit('login', res);
@ -153,37 +153,37 @@ function queryKey() {
type: 'error', type: 'error',
text: i18n.ts.signinFailed text: i18n.ts.signinFailed
}); });
signing = false; signing.value = false;
}); });
} }
function onSubmit() { function onSubmit() {
signing = true; signing.value = true;
console.log('submit'); console.log('submit');
if (!totpLogin && user && user.twoFactorEnabled) { if (!totpLogin.value && user.value && user.value.twoFactorEnabled) {
if (window.PublicKeyCredential && user.securityKeys) { if (window.PublicKeyCredential && user.value.securityKeys) {
os.api('signin', { os.api('signin', {
username, username: username.value,
password, password: password.value,
'hcaptcha-response': hCaptchaResponse, 'hcaptcha-response': hCaptchaResponse.value,
'g-recaptcha-response': reCaptchaResponse, 'g-recaptcha-response': reCaptchaResponse.value,
}).then(res => { }).then(res => {
totpLogin = true; totpLogin.value = true;
signing = false; signing.value = false;
challengeData = res; challengeData.value = res;
return queryKey(); return queryKey();
}).catch(loginFailed); }).catch(loginFailed);
} else { } else {
totpLogin = true; totpLogin.value = true;
signing = false; signing.value = false;
} }
} else { } else {
os.api('signin', { os.api('signin', {
username, username: username.value,
password, password: password.value,
'hcaptcha-response': hCaptchaResponse, 'hcaptcha-response': hCaptchaResponse.value,
'g-recaptcha-response': reCaptchaResponse, 'g-recaptcha-response': reCaptchaResponse.value,
token: user && user.twoFactorEnabled ? token : undefined token: user.value && user.value.twoFactorEnabled ? token.value : undefined
}).then(res => { }).then(res => {
emit('login', res); emit('login', res);
onLogin(res); onLogin(res);
@ -231,9 +231,9 @@ function loginFailed(err) {
} }
} }
challengeData = null; challengeData.value = null;
totpLogin = false; totpLogin.value = false;
signing = false; signing.value = false;
} }
function resetPassword() { function resetPassword() {

View File

@ -13,7 +13,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import MkSignin from '@/components/MkSignin.vue'; import MkSignin from '@/components/MkSignin.vue';
import XModalWindow from '@/components/MkModalWindow.vue'; import XModalWindow from '@/components/MkModalWindow.vue';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
@ -32,15 +32,15 @@ const emit = defineEmits<{
(ev: 'cancelled'): void; (ev: 'cancelled'): void;
}>(); }>();
const dialog = $ref<InstanceType<typeof XModalWindow>>(); const dialog = ref<InstanceType<typeof XModalWindow>>();
function onClose() { function onClose() {
emit('cancelled'); emit('cancelled');
dialog.close(); dialog.value.close();
} }
function onLogin(res) { function onLogin(res) {
emit('done', res); emit('done', res);
dialog.close(); dialog.value.close();
} }
</script> </script>

View File

@ -65,7 +65,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref, computed } from 'vue';
import getPasswordStrength from 'syuilo-password-strength'; import getPasswordStrength from 'syuilo-password-strength';
import { toUnicode } from 'punycode/'; import { toUnicode } from 'punycode/';
import MkButton from './MkButton.vue'; import MkButton from './MkButton.vue';
@ -91,73 +91,73 @@ const emit = defineEmits<{
const host = toUnicode(config.host); const host = toUnicode(config.host);
let hcaptcha = $ref(); let hcaptcha = ref();
let recaptcha = $ref(); let recaptcha = ref();
let username: string = $ref(''); let username: string = ref('');
let password: string = $ref(''); let password: string = ref('');
let retypedPassword: string = $ref(''); let retypedPassword: string = ref('');
let invitationCode: string = $ref(''); let invitationCode: string = ref('');
let email = $ref(''); let email = ref('');
let usernameState: null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range' = $ref(null); let usernameState: null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range' = ref(null);
let emailState: null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error' = $ref(null); let emailState: null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error' = ref(null);
let passwordStrength: '' | 'low' | 'medium' | 'high' = $ref(''); let passwordStrength: '' | 'low' | 'medium' | 'high' = ref('');
let passwordRetypeState: null | 'match' | 'not-match' = $ref(null); let passwordRetypeState: null | 'match' | 'not-match' = ref(null);
let submitting: boolean = $ref(false); let submitting: boolean = ref(false);
let ToSAgreement: boolean = $ref(false); let ToSAgreement: boolean = ref(false);
let hCaptchaResponse = $ref(null); let hCaptchaResponse = ref(null);
let reCaptchaResponse = $ref(null); let reCaptchaResponse = ref(null);
const shouldDisableSubmitting = $computed((): boolean => { const shouldDisableSubmitting = computed((): boolean => {
return submitting || return submitting.value ||
instance.tosUrl && !ToSAgreement || instance.tosUrl && !ToSAgreement.value ||
instance.enableHcaptcha && !hCaptchaResponse || instance.enableHcaptcha && !hCaptchaResponse.value ||
instance.enableRecaptcha && !reCaptchaResponse || instance.enableRecaptcha && !reCaptchaResponse.value ||
passwordRetypeState === 'not-match'; passwordRetypeState.value === 'not-match';
}); });
function onChangeUsername(): void { function onChangeUsername(): void {
if (username === '') { if (username.value === '') {
usernameState = null; usernameState.value = null;
return; return;
} }
{ {
const err = const err =
!username.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' : !username.value.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' :
username.length < 1 ? 'min-range' : username.value.length < 1 ? 'min-range' :
username.length > 20 ? 'max-range' : username.value.length > 20 ? 'max-range' :
null; null;
if (err) { if (err) {
usernameState = err; usernameState.value = err;
return; return;
} }
} }
usernameState = 'wait'; usernameState.value = 'wait';
os.api('username/available', { os.api('username/available', {
username, username: username.value,
}).then(result => { }).then(result => {
usernameState = result.available ? 'ok' : 'unavailable'; usernameState.value = result.available ? 'ok' : 'unavailable';
}).catch(() => { }).catch(() => {
usernameState = 'error'; usernameState.value = 'error';
}); });
} }
function onChangeEmail(): void { function onChangeEmail(): void {
if (email === '') { if (email.value === '') {
emailState = null; emailState.value = null;
return; return;
} }
emailState = 'wait'; emailState.value = 'wait';
os.api('email-address/available', { os.api('email-address/available', {
emailAddress: email, emailAddress: email.value,
}).then(result => { }).then(result => {
emailState = result.available ? 'ok' : emailState.value = result.available ? 'ok' :
result.reason === 'used' ? 'unavailable:used' : result.reason === 'used' ? 'unavailable:used' :
result.reason === 'format' ? 'unavailable:format' : result.reason === 'format' ? 'unavailable:format' :
result.reason === 'disposable' ? 'unavailable:disposable' : result.reason === 'disposable' ? 'unavailable:disposable' :
@ -165,52 +165,52 @@ function onChangeEmail(): void {
result.reason === 'smtp' ? 'unavailable:smtp' : result.reason === 'smtp' ? 'unavailable:smtp' :
'unavailable'; 'unavailable';
}).catch(() => { }).catch(() => {
emailState = 'error'; emailState.value = 'error';
}); });
} }
function onChangePassword(): void { function onChangePassword(): void {
if (password === '') { if (password.value === '') {
passwordStrength = ''; passwordStrength.value = '';
return; return;
} }
const strength = getPasswordStrength(password); const strength = getPasswordStrength(password.value);
passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low'; passwordStrength.value = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low';
} }
function onChangePasswordRetype(): void { function onChangePasswordRetype(): void {
if (retypedPassword === '') { if (retypedPassword.value === '') {
passwordRetypeState = null; passwordRetypeState.value = null;
return; return;
} }
passwordRetypeState = password === retypedPassword ? 'match' : 'not-match'; passwordRetypeState.value = password.value === retypedPassword.value ? 'match' : 'not-match';
} }
function onSubmit(): void { function onSubmit(): void {
if (submitting) return; if (submitting.value) return;
submitting = true; submitting.value = true;
os.api('signup', { os.api('signup', {
username, username: username.value,
password, password: password.value,
emailAddress: email, emailAddress: email.value,
invitationCode, invitationCode: invitationCode.value,
'hcaptcha-response': hCaptchaResponse, 'hcaptcha-response': hCaptchaResponse.value,
'g-recaptcha-response': reCaptchaResponse, 'g-recaptcha-response': reCaptchaResponse.value,
}).then(() => { }).then(() => {
if (instance.emailRequiredForSignup) { if (instance.emailRequiredForSignup) {
os.alert({ os.alert({
type: 'success', type: 'success',
title: i18n.ts._signup.almostThere, title: i18n.ts._signup.almostThere,
text: i18n.t('_signup.emailSent', { email }), text: i18n.t('_signup.emailSent', { email: email.value }),
}); });
emit('signupEmailPending'); emit('signupEmailPending');
} else { } else {
os.api('signin', { os.api('signin', {
username, username: username.value,
password, password: password.value,
}).then(res => { }).then(res => {
emit('signup', res); emit('signup', res);
@ -220,9 +220,9 @@ function onSubmit(): void {
}); });
} }
}).catch(() => { }).catch(() => {
submitting = false; submitting.value = false;
hcaptcha.reset?.(); hcaptcha.value.reset?.();
recaptcha.reset?.(); recaptcha.value.reset?.();
os.alert({ os.alert({
type: 'error', type: 'error',

View File

@ -17,7 +17,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import XSignup from '@/components/MkSignup.vue'; import XSignup from '@/components/MkSignup.vue';
import XModalWindow from '@/components/MkModalWindow.vue'; import XModalWindow from '@/components/MkModalWindow.vue';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
@ -33,14 +33,14 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const dialog = $ref<InstanceType<typeof XModalWindow>>(); const dialog = ref<InstanceType<typeof XModalWindow>>();
function onSignup(res) { function onSignup(res) {
emit('done', res); emit('done', res);
dialog.close(); dialog.value.close();
} }
function onSignupEmailPending() { function onSignupEmailPending() {
dialog.close(); dialog.value.close();
} }
</script> </script>

View File

@ -22,7 +22,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import XMediaList from '@/components/MkMediaList.vue'; import XMediaList from '@/components/MkMediaList.vue';
import XPoll from '@/components/MkPoll.vue'; import XPoll from '@/components/MkPoll.vue';
@ -32,7 +32,7 @@ const props = defineProps<{
note: misskey.entities.Note; note: misskey.entities.Note;
}>(); }>();
const collapsed = $ref( const collapsed = ref(
props.note.cw == null && props.note.text != null && ( props.note.cw == null && props.note.text != null && (
(props.note.text.split('\n').length > 9) || (props.note.text.split('\n').length > 9) ||
(props.note.text.length > 500) (props.note.text.length > 500)

View File

@ -18,13 +18,13 @@ const SAFE_FOR_HTML_ID = 'abcdefghijklmnopqrstuvwxyz';
const computedStyle = getComputedStyle(document.documentElement); const computedStyle = getComputedStyle(document.documentElement);
const idForCanvas = Array.from(Array(16)).map(() => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join(''); const idForCanvas = Array.from(Array(16)).map(() => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
const idForTags = Array.from(Array(16)).map(() => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join(''); const idForTags = Array.from(Array(16)).map(() => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
let available = $ref(false); let available = ref(false);
let rootEl = $ref<HTMLElement | null>(null); let rootEl = ref<HTMLElement | null>(null);
let canvasEl = $ref<HTMLCanvasElement | null>(null); let canvasEl = ref<HTMLCanvasElement | null>(null);
let tagsEl = $ref<HTMLElement | null>(null); let tagsEl = ref<HTMLElement | null>(null);
let width = $ref(300); let width = ref(300);
watch($$(available), () => { watch((available), () => {
try { try {
window.TagCanvas.Start(idForCanvas, idForTags, { window.TagCanvas.Start(idForCanvas, idForTags, {
textColour: '#ffffff', textColour: '#ffffff',
@ -47,15 +47,15 @@ watch($$(available), () => {
}); });
onMounted(() => { onMounted(() => {
width = rootEl.offsetWidth; width.value = rootEl.value.offsetWidth;
if (loaded) { if (loaded) {
available = true; available.value = true;
} else { } else {
document.head.appendChild(Object.assign(document.createElement('script'), { document.head.appendChild(Object.assign(document.createElement('script'), {
async: true, async: true,
src: '/client-assets/tagcanvas.min.js', src: '/client-assets/tagcanvas.min.js',
})).addEventListener('load', () => available = true); })).addEventListener('load', () => available.value = true);
} }
}); });

View File

@ -25,10 +25,10 @@ const emit = defineEmits<{
provide('inChannel', computed(() => props.src === 'channel')); provide('inChannel', computed(() => props.src === 'channel'));
const tlComponent: InstanceType<typeof XNotes> = $ref(); const tlComponent: InstanceType<typeof XNotes> = ref();
const prepend = note => { const prepend = note => {
tlComponent.pagingComponent?.prepend(note); tlComponent.value.pagingComponent?.prepend(note);
emit('note'); emit('note');
@ -38,16 +38,16 @@ const prepend = note => {
}; };
const onUserAdded = () => { const onUserAdded = () => {
tlComponent.pagingComponent?.reload(); tlComponent.value.pagingComponent?.reload();
}; };
const onUserRemoved = () => { const onUserRemoved = () => {
tlComponent.pagingComponent?.reload(); tlComponent.value.pagingComponent?.reload();
}; };
const onChangeFollowing = () => { const onChangeFollowing = () => {
if (!tlComponent.pagingComponent?.backed) { if (!tlComponent.value.pagingComponent?.backed) {
tlComponent.pagingComponent?.reload(); tlComponent.value.pagingComponent?.reload();
} }
}; };

View File

@ -23,11 +23,11 @@ const emit = defineEmits<{
}>(); }>();
const zIndex = os.claimZIndex('high'); const zIndex = os.claimZIndex('high');
let showing = $ref(true); let showing = ref(true);
onMounted(() => { onMounted(() => {
window.setTimeout(() => { window.setTimeout(() => {
showing = false; showing.value = false;
}, 4000); }, 4000);
}); });
</script> </script>

View File

@ -29,7 +29,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import { permissions as kinds } from 'misskey-js'; import { permissions as kinds } from 'misskey-js';
import MkInput from './form/input.vue'; import MkInput from './form/input.vue';
import MkSwitch from './form/switch.vue'; import MkSwitch from './form/switch.vue';
@ -54,37 +54,37 @@ const emit = defineEmits<{
(ev: 'done', result: { name: string | null, permissions: string[] }): void; (ev: 'done', result: { name: string | null, permissions: string[] }): void;
}>(); }>();
const dialog = $ref<InstanceType<typeof XModalWindow>>(); const dialog = ref<InstanceType<typeof XModalWindow>>();
let name = $ref(props.initialName); let name = ref(props.initialName);
let permissions = $ref({}); let permissions = ref({});
if (props.initialPermissions) { if (props.initialPermissions) {
for (const kind of props.initialPermissions) { for (const kind of props.initialPermissions) {
permissions[kind] = true; permissions.value[kind] = true;
} }
} else { } else {
for (const kind of kinds) { for (const kind of kinds) {
permissions[kind] = false; permissions.value[kind] = false;
} }
} }
function ok(): void { function ok(): void {
emit('done', { emit('done', {
name: name, name: name.value,
permissions: Object.keys(permissions).filter(p => permissions[p]), permissions: Object.keys(permissions.value).filter(p => permissions.value[p]),
}); });
dialog.close(); dialog.value.close();
} }
function disableAll(): void { function disableAll(): void {
for (const p in permissions) { for (const p in permissions.value) {
permissions[p] = false; permissions.value[p] = false;
} }
} }
function enableAll(): void { function enableAll(): void {
for (const p in permissions) { for (const p in permissions.value) {
permissions[p] = true; permissions.value[p] = true;
} }
} }
</script> </script>

View File

@ -33,7 +33,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent, onMounted, onUnmounted } from 'vue'; import { defineAsyncComponent, onMounted, onUnmounted, ref } from 'vue';
import { url as local, lang } from '@/config'; import { url as local, lang } from '@/config';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import * as os from '@/os'; import * as os from '@/os';
@ -49,33 +49,33 @@ const props = withDefaults(defineProps<{
}); });
const MOBILE_THRESHOLD = 500; const MOBILE_THRESHOLD = 500;
const isMobile = $ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD); const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD);
const self = props.url.startsWith(local); const self = props.url.startsWith(local);
const attr = self ? 'to' : 'href'; const attr = self ? 'to' : 'href';
const target = self ? null : '_blank'; const target = self ? null : '_blank';
let fetching = $ref(true); let fetching = ref(true);
let title = $ref<string | null>(null); let title = ref<string | null>(null);
let description = $ref<string | null>(null); let description = ref<string | null>(null);
let thumbnail = $ref<string | null>(null); let thumbnail = ref<string | null>(null);
let icon = $ref<string | null>(null); let icon = ref<string | null>(null);
let sitename = $ref<string | null>(null); let sitename = ref<string | null>(null);
let player = $ref({ let player = ref({
url: null, url: null,
width: null, width: null,
height: null, height: null,
}); });
let playerEnabled = $ref(false); let playerEnabled = ref(false);
let tweetId = $ref<string | null>(null); let tweetId = ref<string | null>(null);
let tweetExpanded = $ref(props.detail); let tweetExpanded = ref(props.detail);
const embedId = `embed${Math.random().toString().replace(/\D/,'')}`; const embedId = `embed${Math.random().toString().replace(/\D/,'')}`;
let tweetHeight = $ref(150); let tweetHeight = ref(150);
const requestUrl = new URL(props.url); const requestUrl = new URL(props.url);
if (requestUrl.hostname === 'twitter.com' || requestUrl.hostname === 'mobile.twitter.com') { if (requestUrl.hostname === 'twitter.com' || requestUrl.hostname === 'mobile.twitter.com') {
const m = requestUrl.pathname.match(/^\/.+\/status(?:es)?\/(\d+)/); const m = requestUrl.pathname.match(/^\/.+\/status(?:es)?\/(\d+)/);
if (m) tweetId = m[1]; if (m) tweetId.value = m[1];
} }
if (requestUrl.hostname === 'music.youtube.com' && requestUrl.pathname.match('^/(?:watch|channel)')) { if (requestUrl.hostname === 'music.youtube.com' && requestUrl.pathname.match('^/(?:watch|channel)')) {
@ -89,13 +89,13 @@ requestUrl.hash = '';
fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${requestLang}`).then(res => { fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${requestLang}`).then(res => {
res.json().then(info => { res.json().then(info => {
if (info.url == null) return; if (info.url == null) return;
title = info.title; title.value = info.title;
description = info.description; description.value = info.description;
thumbnail = info.thumbnail; thumbnail.value = info.thumbnail;
icon = info.icon; icon.value = info.icon;
sitename = info.sitename; sitename.value = info.sitename;
fetching = false; fetching.value = false;
player = info.player; player.value = info.player;
}); });
}); });
@ -105,7 +105,7 @@ function adjustTweetHeight(message: any) {
if (embed?.method !== 'twttr.private.resize') return; if (embed?.method !== 'twttr.private.resize') return;
if (embed?.id !== embedId) return; if (embed?.id !== embedId) return;
const height = embed?.params[0]?.height; const height = embed?.params[0]?.height;
if (height) tweetHeight = height; if (height) tweetHeight.value = height;
} }
const openPlayer = (): void => { const openPlayer = (): void => {

View File

@ -7,7 +7,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted, ref } from 'vue';
import MkUrlPreview from '@/components/MkUrlPreview.vue'; import MkUrlPreview from '@/components/MkUrlPreview.vue';
import * as os from '@/os'; import * as os from '@/os';
@ -22,16 +22,16 @@ const emit = defineEmits<{
}>(); }>();
const zIndex = os.claimZIndex('middle'); const zIndex = os.claimZIndex('middle');
let top = $ref(0); let top = ref(0);
let left = $ref(0); let left = ref(0);
onMounted(() => { onMounted(() => {
const rect = props.source.getBoundingClientRect(); const rect = props.source.getBoundingClientRect();
const x = Math.max((rect.left + (props.source.offsetWidth / 2)) - (300 / 2), 6) + window.pageXOffset; const x = Math.max((rect.left + (props.source.offsetWidth / 2)) - (300 / 2), 6) + window.pageXOffset;
const y = rect.top + props.source.offsetHeight + window.pageYOffset; const y = rect.top + props.source.offsetHeight + window.pageYOffset;
top = y; top.value = y;
left = x; left.value = x;
}); });
</script> </script>

View File

@ -10,6 +10,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import MkMiniChart from '@/components/MkMiniChart.vue'; import MkMiniChart from '@/components/MkMiniChart.vue';
import * as os from '@/os'; import * as os from '@/os';
@ -19,12 +21,12 @@ const props = defineProps<{
user: misskey.entities.User; user: misskey.entities.User;
}>(); }>();
let chartValues = $ref<number[] | null>(null); let chartValues = ref<number[] | null>(null);
os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16 + 1, span: 'day' }).then(res => { os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16 + 1, span: 'day' }).then(res => {
// //
res.inc.splice(0, 1); res.inc.splice(0, 1);
chartValues = res.inc; chartValues.value = res.inc;
}); });
</script> </script>

View File

@ -3,7 +3,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { computed } from 'vue';
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
@ -11,7 +11,7 @@ const props = defineProps<{
user: misskey.entities.User; user: misskey.entities.User;
}>(); }>();
const text = $computed(() => { const text = computed(() => {
switch (props.user.onlineStatus) { switch (props.user.onlineStatus) {
case 'online': return i18n.ts.online; case 'online': return i18n.ts.online;
case 'active': return i18n.ts.active; case 'active': return i18n.ts.active;

View File

@ -34,7 +34,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted, ref } from 'vue';
import * as Acct from 'misskey-js/built/acct'; import * as Acct from 'misskey-js/built/acct';
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import MkFollowButton from '@/components/MkFollowButton.vue'; import MkFollowButton from '@/components/MkFollowButton.vue';
@ -54,13 +54,13 @@ const emit = defineEmits<{
}>(); }>();
const zIndex = os.claimZIndex('middle'); const zIndex = os.claimZIndex('middle');
let user = $ref<misskey.entities.UserDetailed | null>(null); let user = ref<misskey.entities.UserDetailed | null>(null);
let top = $ref(0); let top = ref(0);
let left = $ref(0); let left = ref(0);
onMounted(() => { onMounted(() => {
if (typeof props.q === 'object') { if (typeof props.q === 'object') {
user = props.q; user.value = props.q;
} else { } else {
const query = props.q.startsWith('@') ? const query = props.q.startsWith('@') ?
Acct.parse(props.q.substr(1)) : Acct.parse(props.q.substr(1)) :
@ -68,7 +68,7 @@ onMounted(() => {
os.api('users/show', query).then(res => { os.api('users/show', query).then(res => {
if (!props.showing) return; if (!props.showing) return;
user = res; user.value = res;
}); });
} }
@ -76,8 +76,8 @@ onMounted(() => {
const x = ((rect.left + (props.source.offsetWidth / 2)) - (300 / 2)) + window.pageXOffset; const x = ((rect.left + (props.source.offsetWidth / 2)) - (300 / 2)) + window.pageXOffset;
const y = rect.top + props.source.offsetHeight + window.pageYOffset; const y = rect.top + props.source.offsetHeight + window.pageYOffset;
top = y; top.value = y;
left = x; left.value = x;
}); });
</script> </script>

View File

@ -52,7 +52,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted } from 'vue'; import { nextTick, onMounted, ref } from 'vue';
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
import FormSplit from '@/components/form/split.vue'; import FormSplit from '@/components/form/split.vue';
@ -67,50 +67,50 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
let username = $ref(''); let username = ref('');
let host = $ref(''); let host = ref('');
let users: misskey.entities.UserDetailed[] = $ref([]); let users: misskey.entities.UserDetailed[] = ref([]);
let recentUsers: misskey.entities.UserDetailed[] = $ref([]); let recentUsers: misskey.entities.UserDetailed[] = ref([]);
let selected: misskey.entities.UserDetailed | null = $ref(null); let selected: misskey.entities.UserDetailed | null = ref(null);
let dialogEl = $ref(); let dialogEl = ref();
const search = () => { const search = () => {
if (username === '' && host === '') { if (username.value === '' && host.value === '') {
users = []; users.value = [];
return; return;
} }
os.api('users/search-by-username-and-host', { os.api('users/search-by-username-and-host', {
username: username, username: username.value,
host: host, host: host.value,
limit: 10, limit: 10,
detail: false, detail: false,
}).then(_users => { }).then(_users => {
users = _users; users.value = _users;
}); });
}; };
const ok = () => { const ok = () => {
if (selected == null) return; if (selected.value == null) return;
emit('ok', selected); emit('ok', selected.value);
dialogEl.close(); dialogEl.value.close();
// 使 // 使
let recents = defaultStore.state.recentlyUsedUsers; let recents = defaultStore.state.recentlyUsedUsers;
recents = recents.filter(x => x !== selected.id); recents = recents.filter(x => x !== selected.value.id);
recents.unshift(selected.id); recents.unshift(selected.value.id);
defaultStore.set('recentlyUsedUsers', recents.splice(0, 16)); defaultStore.set('recentlyUsedUsers', recents.splice(0, 16));
}; };
const cancel = () => { const cancel = () => {
emit('cancel'); emit('cancel');
dialogEl.close(); dialogEl.value.close();
}; };
onMounted(() => { onMounted(() => {
os.api('users/show', { os.api('users/show', {
userIds: defaultStore.state.recentlyUsedUsers, userIds: defaultStore.state.recentlyUsedUsers,
}).then(users => { }).then(users => {
recentUsers = users; recentUsers.value = users;
}); });
}); });
</script> </script>

View File

@ -21,10 +21,10 @@ const props = defineProps<{
}, },
}>(); }>();
const specified = $ref<HTMLElement>(); const specified = ref<HTMLElement>();
if (props.note.visibility === 'specified') { if (props.note.visibility === 'specified') {
useTooltip($$(specified), async (showing) => { useTooltip((specified), async (showing) => {
const users = await os.api('users/show', { const users = await os.api('users/show', {
userIds: props.note.visibleUserIds, userIds: props.note.visibleUserIds,
limit: 10, limit: 10,
@ -34,7 +34,7 @@ if (props.note.visibility === 'specified') {
showing, showing,
users, users,
count: props.note.visibleUserIds.length, count: props.note.visibleUserIds.length,
targetElement: specified, targetElement: specified.value,
}, {}, 'closed'); }, {}, 'closed');
}); });
} }

View File

@ -43,12 +43,12 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, watch } from 'vue'; import { nextTick, watch, ref } from 'vue';
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import MkModal from '@/components/MkModal.vue'; import MkModal from '@/components/MkModal.vue';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
const modal = $ref<InstanceType<typeof MkModal>>(); const modal = ref<InstanceType<typeof MkModal>>();
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
currentVisibility: typeof misskey.noteVisibilities[number]; currentVisibility: typeof misskey.noteVisibilities[number];
@ -63,18 +63,18 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
let v = $ref(props.currentVisibility); let v = ref(props.currentVisibility);
let localOnly = $ref(props.currentLocalOnly); let localOnly = ref(props.currentLocalOnly);
watch($$(localOnly), () => { watch((localOnly), () => {
emit('changeLocalOnly', localOnly); emit('changeLocalOnly', localOnly.value);
}); });
function choose(visibility: typeof misskey.noteVisibilities[number]): void { function choose(visibility: typeof misskey.noteVisibilities[number]): void {
v = visibility; v.value = visibility;
emit('changeVisibility', visibility); emit('changeVisibility', visibility);
nextTick(() => { nextTick(() => {
modal.close(); modal.value.close();
}); });
} }
</script> </script>

View File

@ -35,7 +35,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onBeforeUnmount, onMounted, provide } from 'vue'; import { onBeforeUnmount, onMounted, provide, ref } from 'vue';
import contains from '@/scripts/contains'; import contains from '@/scripts/contains';
import * as os from '@/os'; import * as os from '@/os';
import { MenuItem } from '@/types/menu'; import { MenuItem } from '@/types/menu';
@ -87,17 +87,17 @@ const emit = defineEmits<{
provide('inWindow', true); provide('inWindow', true);
let rootEl = $ref<HTMLElement | null>(); let rootEl = ref<HTMLElement | null>();
let showing = $ref(true); let showing = ref(true);
let beforeClickedAt = 0; let beforeClickedAt = 0;
let maximized = $ref(false); let maximized = ref(false);
let unMaximizedTop = ''; let unMaximizedTop = '';
let unMaximizedLeft = ''; let unMaximizedLeft = '';
let unMaximizedWidth = ''; let unMaximizedWidth = '';
let unMaximizedHeight = ''; let unMaximizedHeight = '';
function close() { function close() {
showing = false; showing.value = false;
} }
function onKeydown(evt) { function onKeydown(evt) {
@ -116,29 +116,29 @@ function onContextmenu(ev: MouseEvent) {
// //
function top() { function top() {
if (rootEl) { if (rootEl.value) {
rootEl.style.zIndex = os.claimZIndex(props.front ? 'middle' : 'low'); rootEl.value.style.zIndex = os.claimZIndex(props.front ? 'middle' : 'low');
} }
} }
function maximize() { function maximize() {
maximized = true; maximized.value = true;
unMaximizedTop = rootEl.style.top; unMaximizedTop = rootEl.value.style.top;
unMaximizedLeft = rootEl.style.left; unMaximizedLeft = rootEl.value.style.left;
unMaximizedWidth = rootEl.style.width; unMaximizedWidth = rootEl.value.style.width;
unMaximizedHeight = rootEl.style.height; unMaximizedHeight = rootEl.value.style.height;
rootEl.style.top = '0'; rootEl.value.style.top = '0';
rootEl.style.left = '0'; rootEl.value.style.left = '0';
rootEl.style.width = '100%'; rootEl.value.style.width = '100%';
rootEl.style.height = '100%'; rootEl.value.style.height = '100%';
} }
function unMaximize() { function unMaximize() {
maximized = false; maximized.value = false;
rootEl.style.top = unMaximizedTop; rootEl.value.style.top = unMaximizedTop;
rootEl.style.left = unMaximizedLeft; rootEl.value.style.left = unMaximizedLeft;
rootEl.style.width = unMaximizedWidth; rootEl.value.style.width = unMaximizedWidth;
rootEl.style.height = unMaximizedHeight; rootEl.value.style.height = unMaximizedHeight;
} }
function onBodyMousedown() { function onBodyMousedown() {
@ -155,7 +155,7 @@ function onHeaderMousedown(evt: MouseEvent) {
let beforeMaximized = false; let beforeMaximized = false;
if (maximized) { if (maximized.value) {
beforeMaximized = true; beforeMaximized = true;
unMaximize(); unMaximize();
} }
@ -169,7 +169,7 @@ function onHeaderMousedown(evt: MouseEvent) {
beforeClickedAt = Date.now(); beforeClickedAt = Date.now();
const main = rootEl; const main = rootEl.value;
if (main == null) return; if (main == null) return;
if (!contains(main, document.activeElement)) main.focus(); if (!contains(main, document.activeElement)) main.focus();
@ -201,8 +201,8 @@ function onHeaderMousedown(evt: MouseEvent) {
// //
if (moveLeft + windowWidth > browserWidth) moveLeft = browserWidth - windowWidth; if (moveLeft + windowWidth > browserWidth) moveLeft = browserWidth - windowWidth;
rootEl.style.left = moveLeft + 'px'; rootEl.value.style.left = moveLeft + 'px';
rootEl.style.top = moveTop + 'px'; rootEl.value.style.top = moveTop + 'px';
} }
if (beforeMaximized) { if (beforeMaximized) {
@ -220,7 +220,7 @@ function onHeaderMousedown(evt: MouseEvent) {
// //
function onTopHandleMousedown(evt) { function onTopHandleMousedown(evt) {
const main = rootEl; const main = rootEl.value;
const base = evt.clientY; const base = evt.clientY;
const height = parseInt(getComputedStyle(main, '').height, 10); const height = parseInt(getComputedStyle(main, '').height, 10);
@ -246,7 +246,7 @@ function onTopHandleMousedown(evt) {
// //
function onRightHandleMousedown(evt) { function onRightHandleMousedown(evt) {
const main = rootEl; const main = rootEl.value;
const base = evt.clientX; const base = evt.clientX;
const width = parseInt(getComputedStyle(main, '').width, 10); const width = parseInt(getComputedStyle(main, '').width, 10);
@ -270,7 +270,7 @@ function onRightHandleMousedown(evt) {
// //
function onBottomHandleMousedown(evt) { function onBottomHandleMousedown(evt) {
const main = rootEl; const main = rootEl.value;
const base = evt.clientY; const base = evt.clientY;
const height = parseInt(getComputedStyle(main, '').height, 10); const height = parseInt(getComputedStyle(main, '').height, 10);
@ -294,7 +294,7 @@ function onBottomHandleMousedown(evt) {
// //
function onLeftHandleMousedown(evt) { function onLeftHandleMousedown(evt) {
const main = rootEl; const main = rootEl.value;
const base = evt.clientX; const base = evt.clientX;
const width = parseInt(getComputedStyle(main, '').width, 10); const width = parseInt(getComputedStyle(main, '').width, 10);
@ -345,27 +345,27 @@ function onBottomLeftHandleMousedown(evt) {
// //
function applyTransformHeight(height) { function applyTransformHeight(height) {
if (height > window.innerHeight) height = window.innerHeight; if (height > window.innerHeight) height = window.innerHeight;
rootEl.style.height = height + 'px'; rootEl.value.style.height = height + 'px';
} }
// //
function applyTransformWidth(width) { function applyTransformWidth(width) {
if (width > window.innerWidth) width = window.innerWidth; if (width > window.innerWidth) width = window.innerWidth;
rootEl.style.width = width + 'px'; rootEl.value.style.width = width + 'px';
} }
// Y // Y
function applyTransformTop(top) { function applyTransformTop(top) {
rootEl.style.top = top + 'px'; rootEl.value.style.top = top + 'px';
} }
// X // X
function applyTransformLeft(left) { function applyTransformLeft(left) {
rootEl.style.left = left + 'px'; rootEl.value.style.left = left + 'px';
} }
function onBrowserResize() { function onBrowserResize() {
const main = rootEl; const main = rootEl.value;
const position = main.getBoundingClientRect(); const position = main.getBoundingClientRect();
const browserWidth = window.innerWidth; const browserWidth = window.innerWidth;
const browserHeight = window.innerHeight; const browserHeight = window.innerHeight;
@ -381,8 +381,8 @@ onMounted(() => {
if (props.initialWidth) applyTransformWidth(props.initialWidth); if (props.initialWidth) applyTransformWidth(props.initialWidth);
if (props.initialHeight) applyTransformHeight(props.initialHeight); if (props.initialHeight) applyTransformHeight(props.initialHeight);
applyTransformTop((window.innerHeight / 2) - (rootEl.offsetHeight / 2)); applyTransformTop((window.innerHeight / 2) - (rootEl.value.offsetHeight / 2));
applyTransformLeft((window.innerWidth / 2) - (rootEl.offsetWidth / 2)); applyTransformLeft((window.innerWidth / 2) - (rootEl.value.offsetWidth / 2));
// //
top(); top();

View File

@ -18,6 +18,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'
import XWindow from '@/components/MkWindow.vue'; import XWindow from '@/components/MkWindow.vue';
import { lang } from '@/config'; import { lang } from '@/config';
@ -27,9 +29,9 @@ const props = defineProps<{
const requestUrl = new URL(props.url); const requestUrl = new URL(props.url);
let fetching = $ref(true); let fetching = ref(true);
let title = $ref<string | null>(null); let title = ref<string | null>(null);
let player = $ref({ let player = ref({
url: null, url: null,
width: null, width: null,
height: null, height: null,
@ -38,13 +40,13 @@ let player = $ref({
const requestLang = (lang ?? 'ja-JP').replace('ja-KS', 'ja-JP'); const requestLang = (lang ?? 'ja-JP').replace('ja-KS', 'ja-JP');
const ytFetch = (): void => { const ytFetch = (): void => {
fetching = true; fetching.value = true;
fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${requestLang}`).then(res => { fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${requestLang}`).then(res => {
res.json().then(info => { res.json().then(info => {
if (info.url == null) return; if (info.url == null) return;
title = info.title; title.value = info.title;
fetching = false; fetching.value = false;
player = info.player; player.value = info.player;
}); });
}); });
}; };

View File

@ -21,7 +21,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { toRefs, Ref } from 'vue'; import { toRefs, Ref, ref } from 'vue';
import * as os from '@/os'; import * as os from '@/os';
import Ripple from '@/components/MkRipple.vue'; import Ripple from '@/components/MkRipple.vue';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
@ -35,16 +35,16 @@ const emit = defineEmits<{
(ev: 'update:modelValue', v: boolean): void; (ev: 'update:modelValue', v: boolean): void;
}>(); }>();
let button = $ref<HTMLElement>(); let button = ref<HTMLElement>();
const checked = toRefs(props).modelValue; const checked = toRefs(props).modelValue;
const toggle = () => { const toggle = () => {
if (props.disabled) return; if (props.disabled) return;
emit('update:modelValue', !checked.value); emit('update:modelValue', !checked.value);
if (!checked.value) { if (!checked.value) {
const rect = button.getBoundingClientRect(); const rect = button.value.getBoundingClientRect();
const x = rect.left + (button.offsetWidth / 2); const x = rect.left + (button.value.offsetWidth / 2);
const y = rect.top + (button.offsetHeight / 2); const y = rect.top + (button.value.offsetHeight / 2);
os.popup(Ripple, { x, y, particle: false }, {}, 'end'); os.popup(Ripple, { x, y, particle: false }, {}, 'end');
} }
}; };

View File

@ -20,19 +20,21 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
defaultOpen: boolean; defaultOpen: boolean;
}>(), { }>(), {
defaultOpen: false, defaultOpen: false,
}); });
let opened = $ref(props.defaultOpen); let opened = ref(props.defaultOpen);
let openedAtLeastOnce = $ref(props.defaultOpen); let openedAtLeastOnce = ref(props.defaultOpen);
const toggle = () => { const toggle = () => {
opened = !opened; opened.value = !opened.value;
if (opened) { if (opened.value) {
openedAtLeastOnce = true; openedAtLeastOnce.value = true;
} }
}; };
</script> </script>

View File

@ -19,7 +19,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { computed } from 'vue';
const props = defineProps<{ const props = defineProps<{
modelValue: any; modelValue: any;
@ -31,7 +31,7 @@ const emit = defineEmits<{
(ev: 'update:modelValue', value: any): void; (ev: 'update:modelValue', value: any): void;
}>(); }>();
let checked = $computed(() => props.modelValue === props.value); let checked = computed(() => props.modelValue === props.value);
function toggle(): void { function toggle(): void {
if (props.disabled) return; if (props.disabled) return;

View File

@ -21,7 +21,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { toRefs, Ref } from 'vue'; import { toRefs, Ref, ref } from 'vue';
import * as os from '@/os'; import * as os from '@/os';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
@ -34,7 +34,7 @@ const emit = defineEmits<{
(ev: 'update:modelValue', v: boolean): void; (ev: 'update:modelValue', v: boolean): void;
}>(); }>();
let button = $ref<HTMLElement>(); let button = ref<HTMLElement>();
const checked = toRefs(props).modelValue; const checked = toRefs(props).modelValue;
const toggle = () => { const toggle = () => {
if (props.disabled) return; if (props.disabled) return;

View File

@ -5,7 +5,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject } from 'vue'; import { inject, computed } from 'vue';
import * as os from '@/os'; import * as os from '@/os';
import copyToClipboard from '@/scripts/copy-to-clipboard'; import copyToClipboard from '@/scripts/copy-to-clipboard';
import { url } from '@/config'; import { url } from '@/config';
@ -24,7 +24,7 @@ const props = withDefaults(defineProps<{
const router = useRouter(); const router = useRouter();
const active = $computed(() => { const active = computed(() => {
if (props.activeClass == null) return false; if (props.activeClass == null) return false;
const resolved = router.resolve(props.to); const resolved = router.resolve(props.to);
if (resolved == null) return false; if (resolved == null) return false;

View File

@ -10,7 +10,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, watch } from 'vue'; import { onMounted, watch, ref } from 'vue';
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import { getStaticImageUrl } from '@/scripts/get-static-image-url'; import { getStaticImageUrl } from '@/scripts/get-static-image-url';
import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash'; import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash';
@ -48,10 +48,10 @@ function onClick(ev: MouseEvent) {
emit('click', ev); emit('click', ev);
} }
let color = $ref(); let color = ref();
watch(() => props.user.avatarBlurhash, () => { watch(() => props.user.avatarBlurhash, () => {
color = extractAvgColorFromBlurhash(props.user.avatarBlurhash); color.value = extractAvgColorFromBlurhash(props.user.avatarBlurhash);
}, { }, {
immediate: true, immediate: true,
}); });

View File

@ -78,21 +78,21 @@ const metadata = injectPageMetadata();
const hideTitle = inject('shouldOmitHeaderTitle', false); const hideTitle = inject('shouldOmitHeaderTitle', false);
const thin_ = props.thin || inject('shouldHeaderThin', false); const thin_ = props.thin || inject('shouldHeaderThin', false);
const el = $ref<HTMLElement | null>(null); const el = ref<HTMLElement | null>(null);
const tabRefs = {}; const tabRefs = {};
const tabHighlightEl = $ref<HTMLElement | null>(null); const tabHighlightEl = ref<HTMLElement | null>(null);
const bg = ref(null); const bg = ref(null);
let narrow = $ref(false); let narrow = ref(false);
const height = ref(0); const height = ref(0);
const hasTabs = $computed(() => props.tabs && props.tabs.length > 0); const hasTabs = computed(() => props.tabs && props.tabs.length > 0);
const hasActions = $computed(() => props.actions && props.actions.length > 0); const hasActions = computed(() => props.actions && props.actions.length > 0);
const show = $computed(() => { const show = computed(() => {
return !hideTitle || hasTabs || hasActions; return !hideTitle || hasTabs.value || hasActions.value;
}); });
const showTabsPopup = (ev: MouseEvent) => { const showTabsPopup = (ev: MouseEvent) => {
if (!hasTabs) return; if (!hasTabs.value) return;
if (!narrow) return; if (!narrow.value) return;
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
const menu = props.tabs.map(tab => ({ const menu = props.tabs.map(tab => ({
@ -111,7 +111,7 @@ const preventDrag = (ev: TouchEvent) => {
}; };
const onClick = () => { const onClick = () => {
scrollToTop(el, { behavior: 'smooth' }); scrollToTop(el.value, { behavior: 'smooth' });
}; };
function onTabMousedown(tab: Tab, ev: MouseEvent): void { function onTabMousedown(tab: Tab, ev: MouseEvent): void {
@ -148,27 +148,27 @@ onMounted(() => {
watch(() => [props.tab, props.tabs], () => { watch(() => [props.tab, props.tabs], () => {
nextTick(() => { nextTick(() => {
const tabEl = tabRefs[props.tab]; const tabEl = tabRefs[props.tab];
if (tabEl && tabHighlightEl) { if (tabEl && tabHighlightEl.value) {
// offsetWidth offsetLeft getBoundingClientRect 使 // offsetWidth offsetLeft getBoundingClientRect 使
// https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4 // https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4
const parentRect = tabEl.parentElement.getBoundingClientRect(); const parentRect = tabEl.parentElement.getBoundingClientRect();
const rect = tabEl.getBoundingClientRect(); const rect = tabEl.getBoundingClientRect();
tabHighlightEl.style.width = rect.width + 'px'; tabHighlightEl.value.style.width = rect.width + 'px';
tabHighlightEl.style.left = (rect.left - parentRect.left) + 'px'; tabHighlightEl.value.style.left = (rect.left - parentRect.left) + 'px';
} }
}); });
}, { }, {
immediate: true, immediate: true,
}); });
if (el && el.parentElement) { if (el.value && el.value.parentElement) {
narrow = el.parentElement.offsetWidth < 500; narrow.value = el.value.parentElement.offsetWidth < 500;
ro = new ResizeObserver((entries, observer) => { ro = new ResizeObserver((entries, observer) => {
if (el.parentElement && document.body.contains(el)) { if (el.value.parentElement && document.body.contains(el.value)) {
narrow = el.parentElement.offsetWidth < 500; narrow.value = el.value.parentElement.offsetWidth < 500;
} }
}); });
ro.observe(el.parentElement); ro.observe(el.value.parentElement);
} }
}); });

View File

@ -21,21 +21,21 @@ const props = withDefaults(defineProps<{
}); });
let ro: ResizeObserver; let ro: ResizeObserver;
let root = $ref<HTMLElement>(); let root = ref<HTMLElement>();
let content = $ref<HTMLElement>(); let content = ref<HTMLElement>();
let margin = $ref(0); let margin = ref(0);
const shouldSpacerMin = inject('shouldSpacerMin', false); const shouldSpacerMin = inject('shouldSpacerMin', false);
const adjust = (rect: { width: number; height: number; }) => { const adjust = (rect: { width: number; height: number; }) => {
if (shouldSpacerMin || deviceKind === 'smartphone') { if (shouldSpacerMin || deviceKind === 'smartphone') {
margin = props.marginMin; margin.value = props.marginMin;
return; return;
} }
if (rect.width > (props.contentMax ?? 0) || (rect.width > 360 && window.innerWidth > 400)) { if (rect.width > (props.contentMax ?? 0) || (rect.width > 360 && window.innerWidth > 400)) {
margin = props.marginMax; margin.value = props.marginMax;
} else { } else {
margin = props.marginMin; margin.value = props.marginMin;
} }
}; };
@ -48,14 +48,14 @@ onMounted(() => {
}); });
*/ */
adjust({ adjust({
width: root!.offsetWidth, width: root.value!.offsetWidth,
height: root!.offsetHeight, height: root.value!.offsetHeight,
}); });
}); });
ro.observe(root!); ro.observe(root.value!);
if (props.contentMax) { if (props.contentMax) {
content!.style.maxWidth = `${props.contentMax}px`; content.value!.style.maxWidth = `${props.contentMax}px`;
} }
}); });

View File

@ -18,18 +18,18 @@ const CURRENT_STICKY_TOP = 'CURRENT_STICKY_TOP';
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, provide, inject, Ref, ref, watch } from 'vue'; import { onMounted, onUnmounted, provide, inject, Ref, ref, watch } from 'vue';
const rootEl = $ref<HTMLElement>(); const rootEl = ref<HTMLElement>();
const headerEl = $ref<HTMLElement>(); const headerEl = ref<HTMLElement>();
const bodyEl = $ref<HTMLElement>(); const bodyEl = ref<HTMLElement>();
let headerHeight = $ref<string | undefined>(); let headerHeight = ref<string | undefined>();
let childStickyTop = $ref(0); let childStickyTop = ref(0);
const parentStickyTop = inject<Ref<number>>(CURRENT_STICKY_TOP, ref(0)); const parentStickyTop = inject<Ref<number>>(CURRENT_STICKY_TOP, ref(0));
provide(CURRENT_STICKY_TOP, $$(childStickyTop)); provide(CURRENT_STICKY_TOP, (childStickyTop));
const calc = () => { const calc = () => {
childStickyTop = parentStickyTop.value + headerEl.offsetHeight; childStickyTop.value = parentStickyTop.value + headerEl.value.offsetHeight;
headerHeight = headerEl.offsetHeight.toString(); headerHeight.value = headerEl.value.offsetHeight.toString();
}; };
const observer = new ResizeObserver(() => { const observer = new ResizeObserver(() => {
@ -43,17 +43,17 @@ onMounted(() => {
watch(parentStickyTop, calc); watch(parentStickyTop, calc);
watch($$(childStickyTop), () => { watch((childStickyTop), () => {
bodyEl.style.setProperty('--stickyTop', `${childStickyTop}px`); bodyEl.value.style.setProperty('--stickyTop', `${childStickyTop.value}px`);
}, { }, {
immediate: true, immediate: true,
}); });
headerEl.style.position = 'sticky'; headerEl.value.style.position = 'sticky';
headerEl.style.top = 'var(--stickyTop, 0)'; headerEl.value.style.top = 'var(--stickyTop, 0)';
headerEl.style.zIndex = '1000'; headerEl.value.style.zIndex = '1000';
observer.observe(headerEl); observer.observe(headerEl.value);
}); });
onUnmounted(() => { onUnmounted(() => {

View File

@ -7,7 +7,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onUnmounted } from 'vue'; import { onUnmounted, shallowRef, computed } from 'vue';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
@ -20,9 +20,9 @@ const props = withDefaults(defineProps<{
const _time = typeof props.time === 'string' ? new Date(props.time) : props.time; const _time = typeof props.time === 'string' ? new Date(props.time) : props.time;
const absolute = _time.toLocaleString(); const absolute = _time.toLocaleString();
let now = $shallowRef(new Date()); let now = shallowRef(new Date());
const relative = $computed(() => { const relative = computed(() => {
const ago = (now.getTime() - _time.getTime()) / 1000/*ms*/; const ago = (now.value.getTime() - _time.getTime()) / 1000/*ms*/;
return ( return (
ago >= 86400 ? _time.toLocaleDateString() : ago >= 86400 ? _time.toLocaleDateString() :
ago >= 3600 ? i18n.t('_ago.hoursAgo', { n: Math.round(ago / 3600).toString() }) : ago >= 3600 ? i18n.t('_ago.hoursAgo', { n: Math.round(ago / 3600).toString() }) :
@ -32,8 +32,8 @@ const relative = $computed(() => {
i18n.ts._ago.future); i18n.ts._ago.future);
}); });
const allRelative = $computed(() => { const allRelative = computed(() => {
const ago = (now.getTime() - _time.getTime()) / 1000/*ms*/; const ago = (now.value.getTime() - _time.getTime()) / 1000/*ms*/;
return ( return (
ago >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago / 31536000).toString() }) : ago >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago / 31536000).toString() }) :
ago >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago / 2592000).toString() }) : ago >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago / 2592000).toString() }) :
@ -48,7 +48,7 @@ const allRelative = $computed(() => {
function tick() { function tick() {
// TODO: // TODO:
now = new Date(); now.value = new Date();
tickId = window.setTimeout(() => { tickId = window.setTimeout(() => {
window.requestAnimationFrame(tick); window.requestAnimationFrame(tick);

View File

@ -11,7 +11,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject, nextTick, onBeforeUnmount, onMounted, onUnmounted, provide, watch } from 'vue'; import { inject, nextTick, onBeforeUnmount, onMounted, onUnmounted, provide, watch, shallowRef, ref } from 'vue';
import { Resolved, Router } from '@/nirax'; import { Resolved, Router } from '@/nirax';
import { defaultStore } from '@/store'; import { defaultStore } from '@/store';
@ -41,16 +41,16 @@ function resolveNested(current: Resolved, d = 0): Resolved | null {
} }
const current = resolveNested(router.current)!; const current = resolveNested(router.current)!;
let currentPageComponent = $shallowRef(current.route.component); let currentPageComponent = shallowRef(current.route.component);
let currentPageProps = $ref(current.props); let currentPageProps = ref(current.props);
let key = $ref(current.route.path + JSON.stringify(Object.fromEntries(current.props))); let key = ref(current.route.path + JSON.stringify(Object.fromEntries(current.props)));
function onChange({ resolved, key: newKey }) { function onChange({ resolved, key: newKey }) {
const current = resolveNested(resolved); const current = resolveNested(resolved);
if (current == null) return; if (current == null) return;
currentPageComponent = current.route.component; currentPageComponent.value = current.route.component;
currentPageProps = current.props; currentPageProps.value = current.props;
key = current.route.path + JSON.stringify(Object.fromEntries(current.props)); key.value = current.route.path + JSON.stringify(Object.fromEntries(current.props));
} }
router.addListener('change', onChange); router.addListener('change', onChange);

View File

@ -18,7 +18,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref, computed } from 'vue';
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import { version } from '@/config'; import { version } from '@/config';
@ -32,29 +32,29 @@ const props = withDefaults(defineProps<{
}>(), { }>(), {
}); });
let loaded = $ref(false); let loaded = ref(false);
let serverIsDead = $ref(false); let serverIsDead = ref(false);
let meta = $ref<misskey.entities.LiteInstanceMetadata | null>(null); let meta = ref<misskey.entities.LiteInstanceMetadata | null>(null);
os.api('meta', { os.api('meta', {
detail: false, detail: false,
}).then(res => { }).then(res => {
loaded = true; loaded.value = true;
serverIsDead = false; serverIsDead.value = false;
meta = res; meta.value = res;
localStorage.setItem('v', res.version); localStorage.setItem('v', res.version);
}, () => { }, () => {
loaded = true; loaded.value = true;
serverIsDead = true; serverIsDead.value = true;
}); });
function reload() { function reload() {
unisonReload(); unisonReload();
} }
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.error, title: i18n.ts.error,

View File

@ -63,7 +63,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onBeforeUnmount } from 'vue'; import { nextTick, onBeforeUnmount, ref, computed } from 'vue';
import { version } from '@/config'; import { version } from '@/config';
import FormLink from '@/components/form/link.vue'; import FormLink from '@/components/form/link.vue';
import FormSection from '@/components/form/section.vue'; import FormSection from '@/components/form/section.vue';
@ -157,15 +157,15 @@ const patrons = [
]; ];
let easterEggReady = false; let easterEggReady = false;
let easterEggEmojis = $ref([]); let easterEggEmojis = ref([]);
let easterEggEngine = $ref(null); let easterEggEngine = ref(null);
const containerEl = $ref<HTMLElement>(); const containerEl = ref<HTMLElement>();
function iconLoaded() { function iconLoaded() {
const emojis = defaultStore.state.reactions; const emojis = defaultStore.state.reactions;
const containerWidth = containerEl.offsetWidth; const containerWidth = containerEl.value.offsetWidth;
for (let i = 0; i < 32; i++) { for (let i = 0; i < 32; i++) {
easterEggEmojis.push({ easterEggEmojis.value.push({
id: i.toString(), id: i.toString(),
top: -(128 + (Math.random() * 256)), top: -(128 + (Math.random() * 256)),
left: (Math.random() * containerWidth), left: (Math.random() * containerWidth),
@ -181,7 +181,7 @@ function iconLoaded() {
function gravity() { function gravity() {
if (!easterEggReady) return; if (!easterEggReady) return;
easterEggReady = false; easterEggReady = false;
easterEggEngine = physics(containerEl); easterEggEngine.value = physics(containerEl.value);
} }
function iLoveMisskey() { function iLoveMisskey() {
@ -192,14 +192,14 @@ function iLoveMisskey() {
} }
onBeforeUnmount(() => { onBeforeUnmount(() => {
if (easterEggEngine) { if (easterEggEngine.value) {
easterEggEngine.stop(); easterEggEngine.value.stop();
} }
}); });
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.aboutMisskey, title: i18n.ts.aboutMisskey,

View File

@ -47,7 +47,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'; import { computed, ref } from 'vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
import MkSelect from '@/components/form/select.vue'; import MkSelect from '@/components/form/select.vue';
@ -57,23 +57,23 @@ import FormSplit from '@/components/form/split.vue';
import * as os from '@/os'; import * as os from '@/os';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
let host = $ref(''); let host = ref('');
let state = $ref('federating'); let state = ref('federating');
let sort = $ref('+pubSub'); let sort = ref('+pubSub');
const pagination = { const pagination = {
endpoint: 'federation/instances' as const, endpoint: 'federation/instances' as const,
limit: 10, limit: 10,
offsetMode: true, offsetMode: true,
params: computed(() => ({ params: computed(() => ({
sort: sort, sort: sort.value,
host: host !== '' ? host : null, host: host.value !== '' ? host.value : null,
...( ...(
state === 'federating' ? { federating: true } : state.value === 'federating' ? { federating: true } :
state === 'subscribing' ? { subscribing: true } : state.value === 'subscribing' ? { subscribing: true } :
state === 'publishing' ? { publishing: true } : state.value === 'publishing' ? { publishing: true } :
state === 'suspended' ? { suspended: true } : state.value === 'suspended' ? { suspended: true } :
state === 'blocked' ? { blocked: true } : state.value === 'blocked' ? { blocked: true } :
state === 'notResponding' ? { notResponding: true } : state.value === 'notResponding' ? { notResponding: true } :
{}), {}),
})), })),
}; };

View File

@ -103,17 +103,17 @@ const props = withDefaults(defineProps<{
initialTab: 'overview', initialTab: 'overview',
}); });
let stats = $ref(null); let stats = ref(null);
let tab = $ref(props.initialTab); let tab = ref(props.initialTab);
const initStats = () => os.api('stats', { const initStats = () => os.api('stats', {
}).then((res) => { }).then((res) => {
stats = res; stats.value = res;
}); });
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => [{ const headerTabs = computed(() => [{
key: 'overview', key: 'overview',
title: i18n.ts.overview, title: i18n.ts.overview,
}, { }, {

View File

@ -62,7 +62,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'; import { computed, ref } from 'vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkSwitch from '@/components/form/switch.vue'; import MkSwitch from '@/components/form/switch.vue';
import MkObjectView from '@/components/MkObjectView.vue'; import MkObjectView from '@/components/MkObjectView.vue';
@ -78,19 +78,19 @@ import { definePageMetadata } from '@/scripts/page-metadata';
import { acct } from '@/filters/user'; import { acct } from '@/filters/user';
import { iAmAdmin, iAmModerator } from '@/account'; import { iAmAdmin, iAmModerator } from '@/account';
let tab = $ref('overview'); let tab = ref('overview');
let file: any = $ref(null); let file: any = ref(null);
let info: any = $ref(null); let info: any = ref(null);
let isSensitive: boolean = $ref(false); let isSensitive: boolean = ref(false);
const props = defineProps<{ const props = defineProps<{
fileId: string, fileId: string,
}>(); }>();
async function fetch() { async function fetch() {
file = await os.api('drive/files/show', { fileId: props.fileId }); file.value = await os.api('drive/files/show', { fileId: props.fileId });
info = await os.api('admin/drive/show-file', { fileId: props.fileId }); info.value = await os.api('admin/drive/show-file', { fileId: props.fileId });
isSensitive = file.isSensitive; isSensitive.value = file.value.isSensitive;
} }
fetch(); fetch();
@ -98,29 +98,29 @@ fetch();
async function del() { async function del() {
const { canceled } = await os.confirm({ const { canceled } = await os.confirm({
type: 'warning', type: 'warning',
text: i18n.t('removeAreYouSure', { x: file.name }), text: i18n.t('removeAreYouSure', { x: file.value.name }),
}); });
if (canceled) return; if (canceled) return;
os.apiWithDialog('drive/files/delete', { os.apiWithDialog('drive/files/delete', {
fileId: file.id, fileId: file.value.id,
}); });
} }
async function toggleIsSensitive(v) { async function toggleIsSensitive(v) {
await os.api('drive/files/update', { fileId: props.fileId, isSensitive: v }); await os.api('drive/files/update', { fileId: props.fileId, isSensitive: v });
isSensitive = v; isSensitive.value = v;
} }
const headerActions = $computed(() => [{ const headerActions = computed(() => [{
text: i18n.ts.openInNewTab, text: i18n.ts.openInNewTab,
icon: 'fas fa-external-link-alt', icon: 'fas fa-external-link-alt',
handler: () => { handler: () => {
window.open(file.url, '_blank'); window.open(file.value.url, '_blank');
}, },
}]); }]);
const headerTabs = $computed(() => [{ const headerTabs = computed(() => [{
key: 'overview', key: 'overview',
title: i18n.ts.overview, title: i18n.ts.overview,
icon: 'fas fa-info-circle', icon: 'fas fa-info-circle',
@ -135,7 +135,7 @@ const headerTabs = $computed(() => [{
}]); }]);
definePageMetadata(computed(() => ({ definePageMetadata(computed(() => ({
title: file ? i18n.ts.file + ': ' + file.name : i18n.ts.file, title: file.value ? i18n.ts.file + ': ' + file.value.name : i18n.ts.file,
icon: 'fas fa-file', icon: 'fas fa-file',
}))); })));
</script> </script>

View File

@ -66,7 +66,7 @@ const metadata = injectPageMetadata();
const el = ref<HTMLElement>(null); const el = ref<HTMLElement>(null);
const tabRefs = {}; const tabRefs = {};
const tabHighlightEl = $ref<HTMLElement | null>(null); const tabHighlightEl = ref<HTMLElement | null>(null);
const bg = ref(null); const bg = ref(null);
const height = ref(0); const height = ref(0);
const hasTabs = computed(() => { const hasTabs = computed(() => {
@ -128,13 +128,13 @@ onMounted(() => {
watch(() => [props.tab, props.tabs], () => { watch(() => [props.tab, props.tabs], () => {
nextTick(() => { nextTick(() => {
const tabEl = tabRefs[props.tab]; const tabEl = tabRefs[props.tab];
if (tabEl && tabHighlightEl) { if (tabEl && tabHighlightEl.value) {
// offsetWidth offsetLeft getBoundingClientRect 使 // offsetWidth offsetLeft getBoundingClientRect 使
// https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4 // https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4
const parentRect = tabEl.parentElement.getBoundingClientRect(); const parentRect = tabEl.parentElement.getBoundingClientRect();
const rect = tabEl.getBoundingClientRect(); const rect = tabEl.getBoundingClientRect();
tabHighlightEl.style.width = rect.width + 'px'; tabHighlightEl.value.style.width = rect.width + 'px';
tabHighlightEl.style.left = (rect.left - parentRect.left) + 'px'; tabHighlightEl.value.style.left = (rect.left - parentRect.left) + 'px';
} }
}); });
}, { }, {

View File

@ -47,7 +47,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'; import { computed, ref } from 'vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
@ -58,31 +58,31 @@ import * as os from '@/os';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
let reports = $ref<InstanceType<typeof MkPagination>>(); let reports = ref<InstanceType<typeof MkPagination>>();
let state = $ref('unresolved'); let state = ref('unresolved');
let reporterOrigin = $ref('combined'); let reporterOrigin = ref('combined');
let targetUserOrigin = $ref('combined'); let targetUserOrigin = ref('combined');
let searchUsername = $ref(''); let searchUsername = ref('');
let searchHost = $ref(''); let searchHost = ref('');
const pagination = { const pagination = {
endpoint: 'admin/abuse-user-reports' as const, endpoint: 'admin/abuse-user-reports' as const,
limit: 10, limit: 10,
params: computed(() => ({ params: computed(() => ({
state, state: state.value,
reporterOrigin, reporterOrigin: reporterOrigin.value,
targetUserOrigin, targetUserOrigin: targetUserOrigin.value,
})), })),
}; };
function resolved(reportId) { function resolved(reportId) {
reports.removeItem(item => item.id === reportId); reports.value.removeItem(item => item.id === reportId);
} }
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.abuseReports, title: i18n.ts.abuseReports,

View File

@ -47,7 +47,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref, computed } from 'vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
@ -58,14 +58,14 @@ import * as os from '@/os';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
let ads: any[] = $ref([]); let ads: any[] = ref([]);
os.api('admin/ad/list').then(adsResponse => { os.api('admin/ad/list').then(adsResponse => {
ads = adsResponse; ads.value = adsResponse;
}); });
function add() { function add() {
ads.unshift({ ads.value.unshift({
id: null, id: null,
memo: '', memo: '',
place: 'square', place: 'square',
@ -83,7 +83,7 @@ function remove(ad) {
text: i18n.t('removeAreYouSure', { x: ad.url }), text: i18n.t('removeAreYouSure', { x: ad.url }),
}).then(({ canceled }) => { }).then(({ canceled }) => {
if (canceled) return; if (canceled) return;
ads = ads.filter(x => x !== ad); ads.value = ads.value.filter(x => x !== ad);
os.apiWithDialog('admin/ad/delete', { os.apiWithDialog('admin/ad/delete', {
id: ad.id, id: ad.id,
}); });
@ -104,14 +104,14 @@ function save(ad) {
} }
} }
const headerActions = $computed(() => [{ const headerActions = computed(() => [{
asFullButton: true, asFullButton: true,
icon: 'fas fa-plus', icon: 'fas fa-plus',
text: i18n.ts.add, text: i18n.ts.add,
handler: add, handler: add,
}]); }]);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.ads, title: i18n.ts.ads,

View File

@ -27,7 +27,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref, computed } from 'vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
@ -36,14 +36,14 @@ import * as os from '@/os';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
let announcements: any[] = $ref([]); let announcements: any[] = ref([]);
os.api('admin/announcements/list').then(announcementResponse => { os.api('admin/announcements/list').then(announcementResponse => {
announcements = announcementResponse; announcements.value = announcementResponse;
}); });
function add() { function add() {
announcements.unshift({ announcements.value.unshift({
id: null, id: null,
title: '', title: '',
text: '', text: '',
@ -57,7 +57,7 @@ function remove(announcement) {
text: i18n.t('removeAreYouSure', { x: announcement.title }), text: i18n.t('removeAreYouSure', { x: announcement.title }),
}).then(({ canceled }) => { }).then(({ canceled }) => {
if (canceled) return; if (canceled) return;
announcements = announcements.filter(x => x !== announcement); announcements.value = announcements.value.filter(x => x !== announcement);
os.api('admin/announcements/delete', announcement); os.api('admin/announcements/delete', announcement);
}); });
} }
@ -90,14 +90,14 @@ function save(announcement) {
} }
} }
const headerActions = $computed(() => [{ const headerActions = computed(() => [{
asFullButton: true, asFullButton: true,
icon: 'fas fa-plus', icon: 'fas fa-plus',
text: i18n.ts.add, text: i18n.ts.add,
handler: add, handler: add,
}]); }]);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.announcements, title: i18n.ts.announcements,

View File

@ -44,7 +44,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent, ref } from 'vue';
import FormRadios from '@/components/form/radios.vue'; import FormRadios from '@/components/form/radios.vue';
import FormInput from '@/components/form/input.vue'; import FormInput from '@/components/form/input.vue';
import FormButton from '@/components/MkButton.vue'; import FormButton from '@/components/MkButton.vue';
@ -56,30 +56,30 @@ import { i18n } from '@/i18n';
const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue')); const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue'));
let provider = $ref(null); let provider = ref(null);
let hcaptchaSiteKey: string | null = $ref(null); let hcaptchaSiteKey: string | null = ref(null);
let hcaptchaSecretKey: string | null = $ref(null); let hcaptchaSecretKey: string | null = ref(null);
let recaptchaSiteKey: string | null = $ref(null); let recaptchaSiteKey: string | null = ref(null);
let recaptchaSecretKey: string | null = $ref(null); let recaptchaSecretKey: string | null = ref(null);
async function init() { async function init() {
const meta = await os.api('admin/meta'); const meta = await os.api('admin/meta');
hcaptchaSiteKey = meta.hcaptchaSiteKey; hcaptchaSiteKey.value = meta.hcaptchaSiteKey;
hcaptchaSecretKey = meta.hcaptchaSecretKey; hcaptchaSecretKey.value = meta.hcaptchaSecretKey;
recaptchaSiteKey = meta.recaptchaSiteKey; recaptchaSiteKey.value = meta.recaptchaSiteKey;
recaptchaSecretKey = meta.recaptchaSecretKey; recaptchaSecretKey.value = meta.recaptchaSecretKey;
provider = meta.enableHcaptcha ? 'hcaptcha' : meta.enableRecaptcha ? 'recaptcha' : null; provider.value = meta.enableHcaptcha ? 'hcaptcha' : meta.enableRecaptcha ? 'recaptcha' : null;
} }
function save() { function save() {
os.apiWithDialog('admin/update-meta', { os.apiWithDialog('admin/update-meta', {
enableHcaptcha: provider === 'hcaptcha', enableHcaptcha: provider.value === 'hcaptcha',
hcaptchaSiteKey, hcaptchaSiteKey: hcaptchaSiteKey.value,
hcaptchaSecretKey, hcaptchaSecretKey: hcaptchaSecretKey.value,
enableRecaptcha: provider === 'recaptcha', enableRecaptcha: provider.value === 'recaptcha',
recaptchaSiteKey, recaptchaSiteKey: recaptchaSiteKey.value,
recaptchaSecretKey, recaptchaSecretKey: recaptchaSecretKey.value,
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });

View File

@ -11,7 +11,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { computed } from 'vue';
import FormSuspense from '@/components/form/suspense.vue'; import FormSuspense from '@/components/form/suspense.vue';
import MkKeyValue from '@/components/MkKeyValue.vue'; import MkKeyValue from '@/components/MkKeyValue.vue';
import * as os from '@/os'; import * as os from '@/os';
@ -22,9 +22,9 @@ import { definePageMetadata } from '@/scripts/page-metadata';
const databasePromiseFactory = () => os.api('admin/get-table-stats').then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size)); const databasePromiseFactory = () => os.api('admin/get-table-stats').then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size));
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.database, title: i18n.ts.database,

View File

@ -46,7 +46,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref, computed } from 'vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import FormSwitch from '@/components/form/switch.vue'; import FormSwitch from '@/components/form/switch.vue';
import FormInput from '@/components/form/input.vue'; import FormInput from '@/components/form/input.vue';
@ -59,23 +59,23 @@ import { fetchInstance, instance } from '@/instance';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
let enableEmail: boolean = $ref(false); let enableEmail: boolean = ref(false);
let email: any = $ref(null); let email: any = ref(null);
let smtpSecure: boolean = $ref(false); let smtpSecure: boolean = ref(false);
let smtpHost: string = $ref(''); let smtpHost: string = ref('');
let smtpPort: number = $ref(0); let smtpPort: number = ref(0);
let smtpUser: string = $ref(''); let smtpUser: string = ref('');
let smtpPass: string = $ref(''); let smtpPass: string = ref('');
async function init() { async function init() {
const meta = await os.api('admin/meta'); const meta = await os.api('admin/meta');
enableEmail = meta.enableEmail; enableEmail.value = meta.enableEmail;
email = meta.email; email.value = meta.email;
smtpSecure = meta.smtpSecure; smtpSecure.value = meta.smtpSecure;
smtpHost = meta.smtpHost; smtpHost.value = meta.smtpHost;
smtpPort = meta.smtpPort; smtpPort.value = meta.smtpPort;
smtpUser = meta.smtpUser; smtpUser.value = meta.smtpUser;
smtpPass = meta.smtpPass; smtpPass.value = meta.smtpPass;
} }
async function testEmail() { async function testEmail() {
@ -94,19 +94,19 @@ async function testEmail() {
function save() { function save() {
os.apiWithDialog('admin/update-meta', { os.apiWithDialog('admin/update-meta', {
enableEmail, enableEmail: enableEmail.value,
email, email: email.value,
smtpSecure, smtpSecure: smtpSecure.value,
smtpHost, smtpHost: smtpHost.value,
smtpPort, smtpPort: smtpPort.value,
smtpUser, smtpUser: smtpUser.value,
smtpPass, smtpPass: smtpPass.value,
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });
} }
const headerActions = $computed(() => [{ const headerActions = computed(() => [{
asFullButton: true, asFullButton: true,
text: i18n.ts.testEmail, text: i18n.ts.testEmail,
handler: testEmail, handler: testEmail,
@ -117,7 +117,7 @@ const headerActions = $computed(() => [{
handler: save, handler: save,
}]); }]);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.emailServer, title: i18n.ts.emailServer,

View File

@ -29,7 +29,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import XModalWindow from '@/components/MkModalWindow.vue'; import XModalWindow from '@/components/MkModalWindow.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
@ -42,11 +42,11 @@ const props = defineProps<{
emoji: any, emoji: any,
}>(); }>();
let dialog = $ref(null); let dialog = ref(null);
let name: string = $ref(props.emoji.name); let name: string = ref(props.emoji.name);
let category: string = $ref(props.emoji.category); let category: string = ref(props.emoji.category);
let aliases: string = $ref(props.emoji.aliases.join(' ')); let aliases: string = ref(props.emoji.aliases.join(' '));
let categories: string[] = $ref(emojiCategories); let categories: string[] = ref(emojiCategories);
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'done', v: { deleted?: boolean, updated?: any }): void, (ev: 'done', v: { deleted?: boolean, updated?: any }): void,
@ -60,27 +60,27 @@ function ok() {
async function update() { async function update() {
await os.apiWithDialog('admin/emoji/update', { await os.apiWithDialog('admin/emoji/update', {
id: props.emoji.id, id: props.emoji.id,
name, name: name.value,
category, category: category.value,
aliases: aliases.split(' '), aliases: aliases.value.split(' '),
}); });
emit('done', { emit('done', {
updated: { updated: {
id: props.emoji.id, id: props.emoji.id,
name, name: name.value,
category, category: category.value,
aliases: aliases.split(' '), aliases: aliases.value.split(' '),
}, },
}); });
dialog.close(); dialog.value.close();
} }
async function del() { async function del() {
const { canceled } = await os.confirm({ const { canceled } = await os.confirm({
type: 'warning', type: 'warning',
text: i18n.t('removeAreYouSure', { x: name }), text: i18n.t('removeAreYouSure', { x: name.value }),
}); });
if (canceled) return; if (canceled) return;
@ -90,7 +90,7 @@ async function del() {
emit('done', { emit('done', {
deleted: true, deleted: true,
}); });
dialog.close(); dialog.value.close();
}); });
} }
</script> </script>

View File

@ -134,7 +134,7 @@ definePageMetadata(computed(() => ({
}))); })));
const tab = ref('string'); const tab = ref('string');
const headerTabs = $computed(() => [{ const headerTabs = computed(() => [{
key: 'string', key: 'string',
title: i18n.ts._simkey.emojiNormal, title: i18n.ts._simkey.emojiNormal,
}, { }, {

View File

@ -271,7 +271,7 @@ const delBulk = async () => {
emojisPaginationComponent.value.reload(); emojisPaginationComponent.value.reload();
}; };
const headerActions = $computed(() => [{ const headerActions = computed(() => [{
asFullButton: true, asFullButton: true,
icon: 'fas fa-plus', icon: 'fas fa-plus',
text: i18n.ts.addEmoji, text: i18n.ts.addEmoji,
@ -281,7 +281,7 @@ const headerActions = $computed(() => [{
handler: menu, handler: menu,
}]); }]);
const headerTabs = $computed(() => [{ const headerTabs = computed(() => [{
key: 'local', key: 'local',
title: i18n.ts.local, title: i18n.ts.local,
}, { }, {

View File

@ -33,7 +33,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineAsyncComponent } from 'vue'; import { computed, defineAsyncComponent, ref } from 'vue';
import * as Acct from 'misskey-js/built/acct'; import * as Acct from 'misskey-js/built/acct';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
@ -45,19 +45,19 @@ import * as os from '@/os';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
let origin = $ref('local'); let origin = ref('local');
let type = $ref(null); let type = ref(null);
let searchHost = $ref(''); let searchHost = ref('');
let userId = $ref(''); let userId = ref('');
let viewMode = $ref('grid'); let viewMode = ref('grid');
const pagination = { const pagination = {
endpoint: 'admin/drive/files' as const, endpoint: 'admin/drive/files' as const,
limit: 10, limit: 10,
params: computed(() => ({ params: computed(() => ({
type: (type && type !== '') ? type : null, type: (type.value && type.value !== '') ? type.value : null,
userId: (userId && userId !== '') ? userId : null, userId: (userId.value && userId.value !== '') ? userId.value : null,
origin: origin, origin: origin.value,
hostname: (searchHost && searchHost !== '') ? searchHost : null, hostname: (searchHost.value && searchHost.value !== '') ? searchHost.value : null,
})), })),
}; };
@ -95,7 +95,7 @@ async function find() {
}); });
} }
const headerActions = $computed(() => [{ const headerActions = computed(() => [{
text: i18n.ts.lookup, text: i18n.ts.lookup,
icon: 'fas fa-search', icon: 'fas fa-search',
handler: find, handler: find,
@ -105,7 +105,7 @@ const headerActions = $computed(() => [{
handler: clear, handler: clear,
}]); }]);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata(computed(() => ({ definePageMetadata(computed(() => ({
title: i18n.ts.files, title: i18n.ts.files,

View File

@ -23,7 +23,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent, inject, nextTick, onMounted, onUnmounted, provide, watch } from 'vue'; import { defineAsyncComponent, inject, nextTick, onMounted, onUnmounted, provide, watch, ref, computed } from 'vue';
import { $i } from '../../account'; import { $i } from '../../account';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import MkSuperMenu from '@/components/MkSuperMenu.vue'; import MkSuperMenu from '@/components/MkSuperMenu.vue';
@ -47,32 +47,32 @@ const indexInfo = {
provide('shouldOmitHeaderTitle', false); provide('shouldOmitHeaderTitle', false);
let INFO = $ref(indexInfo); let INFO = ref(indexInfo);
let childInfo = $ref(null); let childInfo = ref(null);
let narrow = $ref(false); let narrow = ref(false);
let view = $ref(null); let view = ref(null);
let el = $ref(null); let el = ref(null);
let pageProps = $ref({}); let pageProps = ref({});
let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail); let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha; let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha;
let noEmailServer = !instance.enableEmail; let noEmailServer = !instance.enableEmail;
let thereIsUnresolvedAbuseReport = $ref(false); let thereIsUnresolvedAbuseReport = ref(false);
let currentPage = $computed(() => router.currentRef.value.child); let currentPage = computed(() => router.currentRef.value.child);
os.api('admin/abuse-user-reports', { os.api('admin/abuse-user-reports', {
state: 'unresolved', state: 'unresolved',
limit: 1, limit: 1,
}).then(reports => { }).then(reports => {
if (reports.length > 0) thereIsUnresolvedAbuseReport = true; if (reports.length > 0) thereIsUnresolvedAbuseReport.value = true;
}); });
const NARROW_THRESHOLD = 600; const NARROW_THRESHOLD = 600;
const ro = new ResizeObserver((entries, observer) => { const ro = new ResizeObserver((entries, observer) => {
if (entries.length === 0) return; if (entries.length === 0) return;
narrow = entries[0].borderBoxSize[0].inlineSize < NARROW_THRESHOLD; narrow.value = entries[0].borderBoxSize[0].inlineSize < NARROW_THRESHOLD;
}); });
const menuDef = $computed(() => [{ const menuDef = computed(() => [{
title: i18n.ts.quickAction, title: i18n.ts.quickAction,
items: [{ items: [{
type: 'button', type: 'button',
@ -91,52 +91,52 @@ const menuDef = $computed(() => [{
icon: 'fas fa-tachometer-alt', icon: 'fas fa-tachometer-alt',
text: i18n.ts.dashboard, text: i18n.ts.dashboard,
to: '/admin/overview', to: '/admin/overview',
active: currentPage?.route.name === 'overview', active: currentPage.value?.route.name === 'overview',
}, { }, {
icon: 'fas fa-users', icon: 'fas fa-users',
text: i18n.ts.users, text: i18n.ts.users,
to: '/admin/users', to: '/admin/users',
active: currentPage?.route.name === 'users', active: currentPage.value?.route.name === 'users',
}, { }, {
icon: 'fas fa-laugh', icon: 'fas fa-laugh',
text: i18n.ts.customEmojis, text: i18n.ts.customEmojis,
to: '/admin/emojis', to: '/admin/emojis',
active: currentPage?.route.name === 'emojis', active: currentPage.value?.route.name === 'emojis',
}, { }, {
icon: 'fas fa-kiss-wink-heart', icon: 'fas fa-kiss-wink-heart',
text: i18n.ts._simkey.emojiGen, text: i18n.ts._simkey.emojiGen,
to: '/admin/emojigen', to: '/admin/emojigen',
active: currentPage?.route.name === 'emojigen', active: currentPage.value?.route.name === 'emojigen',
}, { }, {
icon: 'fas fa-globe', icon: 'fas fa-globe',
text: i18n.ts.federation, text: i18n.ts.federation,
to: '/about#federation', to: '/about#federation',
active: currentPage?.route.name === 'federation', active: currentPage.value?.route.name === 'federation',
}, { }, {
icon: 'fas fa-clipboard-list', icon: 'fas fa-clipboard-list',
text: i18n.ts.jobQueue, text: i18n.ts.jobQueue,
to: '/admin/queue', to: '/admin/queue',
active: currentPage?.route.name === 'queue', active: currentPage.value?.route.name === 'queue',
}, { }, {
icon: 'fas fa-cloud', icon: 'fas fa-cloud',
text: i18n.ts.files, text: i18n.ts.files,
to: '/admin/files', to: '/admin/files',
active: currentPage?.route.name === 'files', active: currentPage.value?.route.name === 'files',
}, { }, {
icon: 'fas fa-broadcast-tower', icon: 'fas fa-broadcast-tower',
text: i18n.ts.announcements, text: i18n.ts.announcements,
to: '/admin/announcements', to: '/admin/announcements',
active: currentPage?.route.name === 'announcements', active: currentPage.value?.route.name === 'announcements',
}, { }, {
icon: 'fas fa-audio-description', icon: 'fas fa-audio-description',
text: i18n.ts.ads, text: i18n.ts.ads,
to: '/admin/ads', to: '/admin/ads',
active: currentPage?.route.name === 'ads', active: currentPage.value?.route.name === 'ads',
}, { }, {
icon: 'fas fa-exclamation-circle', icon: 'fas fa-exclamation-circle',
text: i18n.ts.abuseReports, text: i18n.ts.abuseReports,
to: '/admin/abuses', to: '/admin/abuses',
active: currentPage?.route.name === 'abuses', active: currentPage.value?.route.name === 'abuses',
}], }],
}, { }, {
title: i18n.ts.settings, title: i18n.ts.settings,
@ -144,47 +144,47 @@ const menuDef = $computed(() => [{
icon: 'fas fa-cog', icon: 'fas fa-cog',
text: i18n.ts.general, text: i18n.ts.general,
to: '/admin/settings', to: '/admin/settings',
active: currentPage?.route.name === 'settings', active: currentPage.value?.route.name === 'settings',
}] : []), ...($i?.isAdmin ? [{ }] : []), ...($i?.isAdmin ? [{
icon: 'fas fa-envelope', icon: 'fas fa-envelope',
text: i18n.ts.emailServer, text: i18n.ts.emailServer,
to: '/admin/email-settings', to: '/admin/email-settings',
active: currentPage?.route.name === 'email-settings', active: currentPage.value?.route.name === 'email-settings',
}] : []), ...($i?.isAdmin ? [{ }] : []), ...($i?.isAdmin ? [{
icon: 'fas fa-cloud', icon: 'fas fa-cloud',
text: i18n.ts.objectStorage, text: i18n.ts.objectStorage,
to: '/admin/object-storage', to: '/admin/object-storage',
active: currentPage?.route.name === 'object-storage', active: currentPage.value?.route.name === 'object-storage',
}] : []), ...($i?.isAdmin ? [{ }] : []), ...($i?.isAdmin ? [{
icon: 'fas fa-lock', icon: 'fas fa-lock',
text: i18n.ts.security, text: i18n.ts.security,
to: '/admin/security', to: '/admin/security',
active: currentPage?.route.name === 'security', active: currentPage.value?.route.name === 'security',
}] : []), { }] : []), {
icon: 'fas fa-globe', icon: 'fas fa-globe',
text: i18n.ts.relays, text: i18n.ts.relays,
to: '/admin/relays', to: '/admin/relays',
active: currentPage?.route.name === 'relays', active: currentPage.value?.route.name === 'relays',
}, ...($i?.isAdmin ? [{ }, ...($i?.isAdmin ? [{
icon: 'fas fa-share-alt', icon: 'fas fa-share-alt',
text: i18n.ts.integration, text: i18n.ts.integration,
to: '/admin/integrations', to: '/admin/integrations',
active: currentPage?.route.name === 'integrations', active: currentPage.value?.route.name === 'integrations',
}] : []), ...($i?.isAdmin ? [{ }] : []), ...($i?.isAdmin ? [{
icon: 'fas fa-ban', icon: 'fas fa-ban',
text: i18n.ts.instanceBlocking, text: i18n.ts.instanceBlocking,
to: '/admin/instance-block', to: '/admin/instance-block',
active: currentPage?.route.name === 'instance-block', active: currentPage.value?.route.name === 'instance-block',
}] : []), ...($i?.isAdmin ? [{ }] : []), ...($i?.isAdmin ? [{
icon: 'fas fa-ghost', icon: 'fas fa-ghost',
text: i18n.ts.proxyAccount, text: i18n.ts.proxyAccount,
to: '/admin/proxy-account', to: '/admin/proxy-account',
active: currentPage?.route.name === 'proxy-account', active: currentPage.value?.route.name === 'proxy-account',
}] : []), ...($i?.isAdmin ? [{ }] : []), ...($i?.isAdmin ? [{
icon: 'fas fa-cogs', icon: 'fas fa-cogs',
text: i18n.ts.other, text: i18n.ts.other,
to: '/admin/other-settings', to: '/admin/other-settings',
active: currentPage?.route.name === 'other-settings', active: currentPage.value?.route.name === 'other-settings',
}] : [])], }] : [])],
}, { }, {
title: i18n.ts.info, title: i18n.ts.info,
@ -192,21 +192,21 @@ const menuDef = $computed(() => [{
icon: 'fas fa-database', icon: 'fas fa-database',
text: i18n.ts.database, text: i18n.ts.database,
to: '/admin/database', to: '/admin/database',
active: currentPage?.route.name === 'database', active: currentPage.value?.route.name === 'database',
}], }],
}]); }]);
watch(narrow, () => { watch(narrow.value, () => {
if (currentPage?.route.name == null && !narrow) { if (currentPage.value?.route.name == null && !narrow.value) {
router.push('/admin/overview'); router.push('/admin/overview');
} }
}); });
onMounted(() => { onMounted(() => {
ro.observe(el); ro.observe(el.value);
narrow = el.offsetWidth < NARROW_THRESHOLD; narrow.value = el.value.offsetWidth < NARROW_THRESHOLD;
if (currentPage?.route.name == null && !narrow) { if (currentPage.value?.route.name == null && !narrow.value) {
router.push('/admin/overview'); router.push('/admin/overview');
} }
}); });
@ -217,9 +217,9 @@ onUnmounted(() => {
provideMetadataReceiver((info) => { provideMetadataReceiver((info) => {
if (info == null) { if (info == null) {
childInfo = null; childInfo.value = null;
} else { } else {
childInfo = info; childInfo.value = info;
} }
}); });
@ -265,11 +265,11 @@ const lookup = (ev) => {
}], ev.currentTarget ?? ev.target); }], ev.currentTarget ?? ev.target);
}; };
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata(INFO); definePageMetadata(INFO.value);
defineExpose({ defineExpose({
header: { header: {

View File

@ -15,7 +15,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref, computed } from 'vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import FormButton from '@/components/MkButton.vue'; import FormButton from '@/components/MkButton.vue';
import FormTextarea from '@/components/form/textarea.vue'; import FormTextarea from '@/components/form/textarea.vue';
@ -25,24 +25,24 @@ import { fetchInstance } from '@/instance';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
let blockedHosts: string = $ref(''); let blockedHosts: string = ref('');
async function init() { async function init() {
const meta = await os.api('admin/meta'); const meta = await os.api('admin/meta');
blockedHosts = meta.blockedHosts.join('\n'); blockedHosts.value = meta.blockedHosts.join('\n');
} }
function save() { function save() {
os.apiWithDialog('admin/update-meta', { os.apiWithDialog('admin/update-meta', {
blockedHosts: blockedHosts.split('\n') || [], blockedHosts: blockedHosts.value.split('\n') || [],
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });
} }
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.instanceBlocking, title: i18n.ts.instanceBlocking,

View File

@ -25,7 +25,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import FormSwitch from '@/components/form/switch.vue'; import FormSwitch from '@/components/form/switch.vue';
import FormInput from '@/components/form/input.vue'; import FormInput from '@/components/form/input.vue';
import FormButton from '@/components/MkButton.vue'; import FormButton from '@/components/MkButton.vue';
@ -35,24 +35,24 @@ import * as os from '@/os';
import { fetchInstance } from '@/instance'; import { fetchInstance } from '@/instance';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
let uri: string = $ref(''); let uri: string = ref('');
let enableDiscordIntegration: boolean = $ref(false); let enableDiscordIntegration: boolean = ref(false);
let discordClientId: string | null = $ref(null); let discordClientId: string | null = ref(null);
let discordClientSecret: string | null = $ref(null); let discordClientSecret: string | null = ref(null);
async function init() { async function init() {
const meta = await os.api('admin/meta'); const meta = await os.api('admin/meta');
uri = meta.uri; uri.value = meta.uri;
enableDiscordIntegration = meta.enableDiscordIntegration; enableDiscordIntegration.value = meta.enableDiscordIntegration;
discordClientId = meta.discordClientId; discordClientId.value = meta.discordClientId;
discordClientSecret = meta.discordClientSecret; discordClientSecret.value = meta.discordClientSecret;
} }
function save() { function save() {
os.apiWithDialog('admin/update-meta', { os.apiWithDialog('admin/update-meta', {
enableDiscordIntegration, enableDiscordIntegration: enableDiscordIntegration.value,
discordClientId, discordClientId: discordClientId.value,
discordClientSecret, discordClientSecret: discordClientSecret.value,
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });

View File

@ -25,7 +25,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref } from 'vue';
import FormSwitch from '@/components/form/switch.vue'; import FormSwitch from '@/components/form/switch.vue';
import FormInput from '@/components/form/input.vue'; import FormInput from '@/components/form/input.vue';
import FormButton from '@/components/MkButton.vue'; import FormButton from '@/components/MkButton.vue';
@ -35,24 +35,24 @@ import * as os from '@/os';
import { fetchInstance } from '@/instance'; import { fetchInstance } from '@/instance';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
let uri: string = $ref(''); let uri: string = ref('');
let enableGithubIntegration: boolean = $ref(false); let enableGithubIntegration: boolean = ref(false);
let githubClientId: string | null = $ref(null); let githubClientId: string | null = ref(null);
let githubClientSecret: string | null = $ref(null); let githubClientSecret: string | null = ref(null);
async function init() { async function init() {
const meta = await os.api('admin/meta'); const meta = await os.api('admin/meta');
uri = meta.uri; uri.value = meta.uri;
enableGithubIntegration = meta.enableGithubIntegration; enableGithubIntegration.value = meta.enableGithubIntegration;
githubClientId = meta.githubClientId; githubClientId.value = meta.githubClientId;
githubClientSecret = meta.githubClientSecret; githubClientSecret.value = meta.githubClientSecret;
} }
function save() { function save() {
os.apiWithDialog('admin/update-meta', { os.apiWithDialog('admin/update-meta', {
enableGithubIntegration, enableGithubIntegration: enableGithubIntegration.value,
githubClientId, githubClientId: githubClientId.value,
githubClientSecret, githubClientSecret: githubClientSecret.value,
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });

View File

@ -25,7 +25,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineComponent } from 'vue'; import { defineComponent, ref } from 'vue';
import FormSwitch from '@/components/form/switch.vue'; import FormSwitch from '@/components/form/switch.vue';
import FormInput from '@/components/form/input.vue'; import FormInput from '@/components/form/input.vue';
import FormButton from '@/components/MkButton.vue'; import FormButton from '@/components/MkButton.vue';
@ -35,24 +35,24 @@ import * as os from '@/os';
import { fetchInstance } from '@/instance'; import { fetchInstance } from '@/instance';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
let uri: string = $ref(''); let uri: string = ref('');
let enableTwitterIntegration: boolean = $ref(false); let enableTwitterIntegration: boolean = ref(false);
let twitterConsumerKey: string | null = $ref(null); let twitterConsumerKey: string | null = ref(null);
let twitterConsumerSecret: string | null = $ref(null); let twitterConsumerSecret: string | null = ref(null);
async function init() { async function init() {
const meta = await os.api('admin/meta'); const meta = await os.api('admin/meta');
uri = meta.uri; uri.value = meta.uri;
enableTwitterIntegration = meta.enableTwitterIntegration; enableTwitterIntegration.value = meta.enableTwitterIntegration;
twitterConsumerKey = meta.twitterConsumerKey; twitterConsumerKey.value = meta.twitterConsumerKey;
twitterConsumerSecret = meta.twitterConsumerSecret; twitterConsumerSecret.value = meta.twitterConsumerSecret;
} }
function save() { function save() {
os.apiWithDialog('admin/update-meta', { os.apiWithDialog('admin/update-meta', {
enableTwitterIntegration, enableTwitterIntegration: enableTwitterIntegration.value,
twitterConsumerKey, twitterConsumerKey: twitterConsumerKey.value,
twitterConsumerSecret, twitterConsumerSecret: twitterConsumerSecret.value,
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });

View File

@ -25,7 +25,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref, computed } from 'vue';
import XTwitter from './integrations.twitter.vue'; import XTwitter from './integrations.twitter.vue';
import XGithub from './integrations.github.vue'; import XGithub from './integrations.github.vue';
import XDiscord from './integrations.discord.vue'; import XDiscord from './integrations.discord.vue';
@ -35,20 +35,20 @@ import * as os from '@/os';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
let enableTwitterIntegration: boolean = $ref(false); let enableTwitterIntegration: boolean = ref(false);
let enableGithubIntegration: boolean = $ref(false); let enableGithubIntegration: boolean = ref(false);
let enableDiscordIntegration: boolean = $ref(false); let enableDiscordIntegration: boolean = ref(false);
async function init() { async function init() {
const meta = await os.api('admin/meta'); const meta = await os.api('admin/meta');
enableTwitterIntegration = meta.enableTwitterIntegration; enableTwitterIntegration.value = meta.enableTwitterIntegration;
enableGithubIntegration = meta.enableGithubIntegration; enableGithubIntegration.value = meta.enableGithubIntegration;
enableDiscordIntegration = meta.enableDiscordIntegration; enableDiscordIntegration.value = meta.enableDiscordIntegration;
} }
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.integration, title: i18n.ts.integration,

View File

@ -69,7 +69,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref, computed } from 'vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import FormSwitch from '@/components/form/switch.vue'; import FormSwitch from '@/components/form/switch.vue';
import FormInput from '@/components/form/input.vue'; import FormInput from '@/components/form/input.vue';
@ -81,65 +81,65 @@ import { fetchInstance } from '@/instance';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
let useObjectStorage: boolean = $ref(false); let useObjectStorage: boolean = ref(false);
let objectStorageBaseUrl: string | null = $ref(null); let objectStorageBaseUrl: string | null = ref(null);
let objectStorageBucket: string | null = $ref(null); let objectStorageBucket: string | null = ref(null);
let objectStoragePrefix: string | null = $ref(null); let objectStoragePrefix: string | null = ref(null);
let objectStorageEndpoint: string | null = $ref(null); let objectStorageEndpoint: string | null = ref(null);
let objectStorageRegion: string | null = $ref(null); let objectStorageRegion: string | null = ref(null);
let objectStoragePort: number | null = $ref(null); let objectStoragePort: number | null = ref(null);
let objectStorageAccessKey: string | null = $ref(null); let objectStorageAccessKey: string | null = ref(null);
let objectStorageSecretKey: string | null = $ref(null); let objectStorageSecretKey: string | null = ref(null);
let objectStorageUseSSL: boolean = $ref(false); let objectStorageUseSSL: boolean = ref(false);
let objectStorageUseProxy: boolean = $ref(false); let objectStorageUseProxy: boolean = ref(false);
let objectStorageSetPublicRead: boolean = $ref(false); let objectStorageSetPublicRead: boolean = ref(false);
let objectStorageS3ForcePathStyle: boolean = $ref(true); let objectStorageS3ForcePathStyle: boolean = ref(true);
async function init() { async function init() {
const meta = await os.api('admin/meta'); const meta = await os.api('admin/meta');
useObjectStorage = meta.useObjectStorage; useObjectStorage.value = meta.useObjectStorage;
objectStorageBaseUrl = meta.objectStorageBaseUrl; objectStorageBaseUrl.value = meta.objectStorageBaseUrl;
objectStorageBucket = meta.objectStorageBucket; objectStorageBucket.value = meta.objectStorageBucket;
objectStoragePrefix = meta.objectStoragePrefix; objectStoragePrefix.value = meta.objectStoragePrefix;
objectStorageEndpoint = meta.objectStorageEndpoint; objectStorageEndpoint.value = meta.objectStorageEndpoint;
objectStorageRegion = meta.objectStorageRegion; objectStorageRegion.value = meta.objectStorageRegion;
objectStoragePort = meta.objectStoragePort; objectStoragePort.value = meta.objectStoragePort;
objectStorageAccessKey = meta.objectStorageAccessKey; objectStorageAccessKey.value = meta.objectStorageAccessKey;
objectStorageSecretKey = meta.objectStorageSecretKey; objectStorageSecretKey.value = meta.objectStorageSecretKey;
objectStorageUseSSL = meta.objectStorageUseSSL; objectStorageUseSSL.value = meta.objectStorageUseSSL;
objectStorageUseProxy = meta.objectStorageUseProxy; objectStorageUseProxy.value = meta.objectStorageUseProxy;
objectStorageSetPublicRead = meta.objectStorageSetPublicRead; objectStorageSetPublicRead.value = meta.objectStorageSetPublicRead;
objectStorageS3ForcePathStyle = meta.objectStorageS3ForcePathStyle; objectStorageS3ForcePathStyle.value = meta.objectStorageS3ForcePathStyle;
} }
function save() { function save() {
os.apiWithDialog('admin/update-meta', { os.apiWithDialog('admin/update-meta', {
useObjectStorage, useObjectStorage: useObjectStorage.value,
objectStorageBaseUrl, objectStorageBaseUrl: objectStorageBaseUrl.value,
objectStorageBucket, objectStorageBucket: objectStorageBucket.value,
objectStoragePrefix, objectStoragePrefix: objectStoragePrefix.value,
objectStorageEndpoint, objectStorageEndpoint: objectStorageEndpoint.value,
objectStorageRegion, objectStorageRegion: objectStorageRegion.value,
objectStoragePort, objectStoragePort: objectStoragePort.value,
objectStorageAccessKey, objectStorageAccessKey: objectStorageAccessKey.value,
objectStorageSecretKey, objectStorageSecretKey: objectStorageSecretKey.value,
objectStorageUseSSL, objectStorageUseSSL: objectStorageUseSSL.value,
objectStorageUseProxy, objectStorageUseProxy: objectStorageUseProxy.value,
objectStorageSetPublicRead, objectStorageSetPublicRead: objectStorageSetPublicRead.value,
objectStorageS3ForcePathStyle, objectStorageS3ForcePathStyle: objectStorageS3ForcePathStyle.value,
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });
} }
const headerActions = $computed(() => [{ const headerActions = computed(() => [{
asFullButton: true, asFullButton: true,
icon: 'fas fa-check', icon: 'fas fa-check',
text: i18n.ts.save, text: i18n.ts.save,
handler: save, handler: save,
}]); }]);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.objectStorage, title: i18n.ts.objectStorage,

View File

@ -10,7 +10,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { computed } from 'vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import FormSuspense from '@/components/form/suspense.vue'; import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os'; import * as os from '@/os';
@ -28,14 +28,14 @@ function save() {
}); });
} }
const headerActions = $computed(() => [{ const headerActions = computed(() => [{
asFullButton: true, asFullButton: true,
icon: 'fas fa-check', icon: 'fas fa-check',
text: i18n.ts.save, text: i18n.ts.save,
handler: save, handler: save,
}]); }]);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.other, title: i18n.ts.other,

View File

@ -10,6 +10,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
import MkMiniChart from '@/components/MkMiniChart.vue'; import MkMiniChart from '@/components/MkMiniChart.vue';
import * as os from '@/os'; import * as os from '@/os';
@ -19,10 +21,10 @@ const props = defineProps<{
user: misskey.entities.User; user: misskey.entities.User;
}>(); }>();
let chart = $ref(null); let chart = ref(null);
os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16, span: 'day' }).then(res => { os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16, span: 'day' }).then(res => {
chart = res; chart.value = res;
}); });
</script> </script>

View File

@ -139,7 +139,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue'; import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick, ref, shallowRef, computed } from 'vue';
import { import {
Chart, Chart,
ArcElement, ArcElement,
@ -196,20 +196,20 @@ Chart.register(
//gradient, //gradient,
); );
const rootEl = $ref<HTMLElement>(); const rootEl = ref<HTMLElement>();
const chartEl = $ref<HTMLCanvasElement>(null); const chartEl = ref<HTMLCanvasElement>(null);
let stats: any = $ref(null); let stats: any = ref(null);
let serverInfo: any = $ref(null); let serverInfo: any = ref(null);
let topSubInstancesForPie: any = $ref(null); let topSubInstancesForPie: any = ref(null);
let topPubInstancesForPie: any = $ref(null); let topPubInstancesForPie: any = ref(null);
let usersComparedToThePrevDay: any = $ref(null); let usersComparedToThePrevDay: any = ref(null);
let notesComparedToThePrevDay: any = $ref(null); let notesComparedToThePrevDay: any = ref(null);
let federationPubActive = $ref<number | null>(null); let federationPubActive = ref<number | null>(null);
let federationPubActiveDiff = $ref<number | null>(null); let federationPubActiveDiff = ref<number | null>(null);
let federationSubActive = $ref<number | null>(null); let federationSubActive = ref<number | null>(null);
let federationSubActiveDiff = $ref<number | null>(null); let federationSubActiveDiff = ref<number | null>(null);
let newUsers = $ref(null); let newUsers = ref(null);
let activeInstances = $shallowRef(null); let activeInstances = shallowRef(null);
const queueStatsConnection = markRaw(stream.useChannel('queueStats')); const queueStatsConnection = markRaw(stream.useChannel('queueStats'));
const now = new Date(); const now = new Date();
let chartInstance: Chart = null; let chartInstance: Chart = null;
@ -252,7 +252,7 @@ async function renderChart() {
const color = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent')); const color = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent'));
chartInstance = new Chart(chartEl, { chartInstance = new Chart(chartEl.value, {
type: 'bar', type: 'bar',
data: { data: {
//labels: new Array(props.limit).fill(0).map((_, i) => getDate(i).toLocaleString()).slice().reverse(), //labels: new Array(props.limit).fill(0).map((_, i) => getDate(i).toLocaleString()).slice().reverse(),
@ -394,26 +394,26 @@ onMounted(async () => {
renderChart(); renderChart();
os.api('stats', {}).then(statsResponse => { os.api('stats', {}).then(statsResponse => {
stats = statsResponse; stats.value = statsResponse;
os.apiGet('charts/users', { limit: 2, span: 'day' }).then(chart => { os.apiGet('charts/users', { limit: 2, span: 'day' }).then(chart => {
usersComparedToThePrevDay = stats.originalUsersCount - chart.local.total[1]; usersComparedToThePrevDay.value = stats.value.originalUsersCount - chart.local.total[1];
}); });
os.apiGet('charts/notes', { limit: 2, span: 'day' }).then(chart => { os.apiGet('charts/notes', { limit: 2, span: 'day' }).then(chart => {
notesComparedToThePrevDay = stats.originalNotesCount - chart.local.total[1]; notesComparedToThePrevDay.value = stats.value.originalNotesCount - chart.local.total[1];
}); });
}); });
os.apiGet('charts/federation', { limit: 2, span: 'day' }).then(chart => { os.apiGet('charts/federation', { limit: 2, span: 'day' }).then(chart => {
federationPubActive = chart.pubActive[0]; federationPubActive.value = chart.pubActive[0];
federationPubActiveDiff = chart.pubActive[0] - chart.pubActive[1]; federationPubActiveDiff.value = chart.pubActive[0] - chart.pubActive[1];
federationSubActive = chart.subActive[0]; federationSubActive.value = chart.subActive[0];
federationSubActiveDiff = chart.subActive[0] - chart.subActive[1]; federationSubActiveDiff.value = chart.subActive[0] - chart.subActive[1];
}); });
os.apiGet('federation/stats', { limit: 10 }).then(res => { os.apiGet('federation/stats', { limit: 10 }).then(res => {
topSubInstancesForPie = res.topSubInstances.map(x => ({ topSubInstancesForPie.value = res.topSubInstances.map(x => ({
name: x.host, name: x.host,
color: x.themeColor, color: x.themeColor,
value: x.followersCount, value: x.followersCount,
@ -421,7 +421,7 @@ onMounted(async () => {
os.pageWindow(`/instance-info/${x.host}`); os.pageWindow(`/instance-info/${x.host}`);
}, },
})).concat([{ name: '(other)', color: '#80808080', value: res.otherFollowersCount }]); })).concat([{ name: '(other)', color: '#80808080', value: res.otherFollowersCount }]);
topPubInstancesForPie = res.topPubInstances.map(x => ({ topPubInstancesForPie.value = res.topPubInstances.map(x => ({
name: x.host, name: x.host,
color: x.themeColor, color: x.themeColor,
value: x.followingCount, value: x.followingCount,
@ -432,21 +432,21 @@ onMounted(async () => {
}); });
os.api('admin/server-info').then(serverInfoResponse => { os.api('admin/server-info').then(serverInfoResponse => {
serverInfo = serverInfoResponse; serverInfo.value = serverInfoResponse;
}); });
os.api('admin/show-users', { os.api('admin/show-users', {
limit: 5, limit: 5,
sort: '+createdAt', sort: '+createdAt',
}).then(res => { }).then(res => {
newUsers = res; newUsers.value = res;
}); });
os.api('federation/instances', { os.api('federation/instances', {
sort: '+lastCommunicatedAt', sort: '+lastCommunicatedAt',
limit: 25, limit: 25,
}).then(res => { }).then(res => {
activeInstances = res; activeInstances.value = res;
}); });
nextTick(() => { nextTick(() => {
@ -461,9 +461,9 @@ onBeforeUnmount(() => {
queueStatsConnection.dispose(); queueStatsConnection.dispose();
}); });
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.dashboard, title: i18n.ts.dashboard,

View File

@ -14,7 +14,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref, computed } from 'vue';
import MkKeyValue from '@/components/MkKeyValue.vue'; import MkKeyValue from '@/components/MkKeyValue.vue';
import FormButton from '@/components/MkButton.vue'; import FormButton from '@/components/MkButton.vue';
import MkInfo from '@/components/MkInfo.vue'; import MkInfo from '@/components/MkInfo.vue';
@ -24,36 +24,36 @@ import { fetchInstance } from '@/instance';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
let proxyAccount: any = $ref(null); let proxyAccount: any = ref(null);
let proxyAccountId: any = $ref(null); let proxyAccountId: any = ref(null);
async function init() { async function init() {
const meta = await os.api('admin/meta'); const meta = await os.api('admin/meta');
proxyAccountId = meta.proxyAccountId; proxyAccountId.value = meta.proxyAccountId;
if (proxyAccountId) { if (proxyAccountId.value) {
proxyAccount = await os.api('users/show', { userId: proxyAccountId }); proxyAccount.value = await os.api('users/show', { userId: proxyAccountId.value });
} }
} }
function chooseProxyAccount() { function chooseProxyAccount() {
os.selectUser().then(user => { os.selectUser().then(user => {
proxyAccount = user; proxyAccount.value = user;
proxyAccountId = user.id; proxyAccountId.value = user.id;
save(); save();
}); });
} }
function save() { function save() {
os.apiWithDialog('admin/update-meta', { os.apiWithDialog('admin/update-meta', {
proxyAccountId: proxyAccountId, proxyAccountId: proxyAccountId.value,
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });
} }
const headerActions = $computed(() => []); const headerActions = computed(() => []);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.proxyAccount, title: i18n.ts.proxyAccount,

View File

@ -53,10 +53,10 @@ const active = ref(0);
const delayed = ref(0); const delayed = ref(0);
const waiting = ref(0); const waiting = ref(0);
const jobs = ref([]); const jobs = ref([]);
let chartProcess = $ref<InstanceType<typeof XChart>>(); let chartProcess = ref<InstanceType<typeof XChart>>();
let chartActive = $ref<InstanceType<typeof XChart>>(); let chartActive = ref<InstanceType<typeof XChart>>();
let chartDelayed = $ref<InstanceType<typeof XChart>>(); let chartDelayed = ref<InstanceType<typeof XChart>>();
let chartWaiting = $ref<InstanceType<typeof XChart>>(); let chartWaiting = ref<InstanceType<typeof XChart>>();
const props = defineProps<{ const props = defineProps<{
domain: string; domain: string;
@ -68,10 +68,10 @@ const onStats = (stats) => {
delayed.value = stats[props.domain].delayed; delayed.value = stats[props.domain].delayed;
waiting.value = stats[props.domain].waiting; waiting.value = stats[props.domain].waiting;
chartProcess.pushData(stats[props.domain].activeSincePrevTick); chartProcess.value.pushData(stats[props.domain].activeSincePrevTick);
chartActive.pushData(stats[props.domain].active); chartActive.value.pushData(stats[props.domain].active);
chartDelayed.pushData(stats[props.domain].delayed); chartDelayed.value.pushData(stats[props.domain].delayed);
chartWaiting.pushData(stats[props.domain].waiting); chartWaiting.value.pushData(stats[props.domain].waiting);
}; };
const onStatsLog = (statsLog) => { const onStatsLog = (statsLog) => {
@ -87,10 +87,10 @@ const onStatsLog = (statsLog) => {
dataWaiting.push(stats[props.domain].waiting); dataWaiting.push(stats[props.domain].waiting);
} }
chartProcess.setData(dataProcess); chartProcess.value.setData(dataProcess);
chartActive.setData(dataActive); chartActive.value.setData(dataActive);
chartDelayed.setData(dataDelayed); chartDelayed.value.setData(dataDelayed);
chartWaiting.setData(dataWaiting); chartWaiting.value.setData(dataWaiting);
}; };
onMounted(() => { onMounted(() => {

View File

@ -9,7 +9,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { markRaw, onMounted, onBeforeUnmount, nextTick } from 'vue'; import { markRaw, onMounted, onBeforeUnmount, nextTick, ref, computed } from 'vue';
import XQueue from './queue.chart.vue'; import XQueue from './queue.chart.vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
@ -18,7 +18,7 @@ import * as config from '@/config';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
let tab = $ref('deliver'); let tab = ref('deliver');
function clear() { function clear() {
os.confirm({ os.confirm({
@ -32,7 +32,7 @@ function clear() {
}); });
} }
const headerActions = $computed(() => [{ const headerActions = computed(() => [{
asFullButton: true, asFullButton: true,
icon: 'fas fa-up-right-from-square', icon: 'fas fa-up-right-from-square',
text: i18n.ts.dashboard, text: i18n.ts.dashboard,
@ -41,7 +41,7 @@ const headerActions = $computed(() => [{
}, },
}]); }]);
const headerTabs = $computed(() => [{ const headerTabs = computed(() => [{
key: 'deliver', key: 'deliver',
title: 'Deliver', title: 'Deliver',
}, { }, {

View File

@ -17,14 +17,14 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue'; import { ref, computed } from 'vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import * as os from '@/os'; import * as os from '@/os';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
let relays: any[] = $ref([]); let relays: any[] = ref([]);
async function addRelay() { async function addRelay() {
const { canceled, result: inbox } = await os.inputText({ const { canceled, result: inbox } = await os.inputText({
@ -60,20 +60,20 @@ function remove(inbox: string) {
function refresh() { function refresh() {
os.api('admin/relays/list').then((relayList: any) => { os.api('admin/relays/list').then((relayList: any) => {
relays = relayList; relays.value = relayList;
}); });
} }
refresh(); refresh();
const headerActions = $computed(() => [{ const headerActions = computed(() => [{
asFullButton: true, asFullButton: true,
icon: 'fas fa-plus', icon: 'fas fa-plus',
text: i18n.ts.addRelay, text: i18n.ts.addRelay,
handler: addRelay, handler: addRelay,
}]); }]);
const headerTabs = $computed(() => []); const headerTabs = computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.relays, title: i18n.ts.relays,

Some files were not shown because too many files have changed in this diff Show More