mirror of
https://github.com/sim1222/misskey.git
synced 2025-04-29 02:37:22 +09:00
commit
733fb0b6bf
@ -1,5 +1,4 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
web:
|
||||
build: .
|
||||
@ -7,7 +6,7 @@ services:
|
||||
links:
|
||||
- db
|
||||
- redis
|
||||
# - es
|
||||
# - es
|
||||
ports:
|
||||
- "3000:3000"
|
||||
networks:
|
||||
@ -16,25 +15,22 @@ services:
|
||||
volumes:
|
||||
- ./files:/misskey/files
|
||||
- ./.config:/misskey/.config:ro
|
||||
|
||||
redis:
|
||||
restart: always
|
||||
image: redis:4.0-alpine
|
||||
image: redis:alpine
|
||||
networks:
|
||||
- internal_network
|
||||
volumes:
|
||||
- ./redis:/data
|
||||
|
||||
db:
|
||||
restart: always
|
||||
image: postgres:12.2-alpine
|
||||
image: postgres:17.0-alpine
|
||||
networks:
|
||||
- internal_network
|
||||
env_file:
|
||||
- .config/docker.env
|
||||
volumes:
|
||||
- ./db:/var/lib/postgresql/data
|
||||
|
||||
# es:
|
||||
# restart: always
|
||||
# image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.2
|
||||
@ -45,7 +41,6 @@ services:
|
||||
# - internal_network
|
||||
# volumes:
|
||||
# - ./elasticsearch:/usr/share/elasticsearch/data
|
||||
|
||||
networks:
|
||||
internal_network:
|
||||
internal: true
|
||||
|
31
package.json
31
package.json
@ -8,8 +8,8 @@
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"postinstall": "node ./scripts/install-packages.js",
|
||||
"build": "node ./scripts/build.js",
|
||||
"postinstall": "node ./scripts/install-packages.mjs",
|
||||
"build": "node ./scripts/build.mjs",
|
||||
"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",
|
||||
"init": "npm run migrate",
|
||||
@ -17,8 +17,8 @@
|
||||
"migrateandstart": "npm run migrate && npm run start",
|
||||
"gulp": "gulp build",
|
||||
"watch": "npm run dev",
|
||||
"dev": "node ./scripts/dev.js",
|
||||
"lint": "node ./scripts/lint.js",
|
||||
"dev": "node ./scripts/dev.mjs",
|
||||
"lint": "node ./scripts/lint.mjs",
|
||||
"cy:open": "cypress open --browser --e2e --config-file=cypress.config.ts",
|
||||
"cy:run": "cypress run",
|
||||
"e2e": "start-server-and-test start:test http://localhost:61812 cy:run",
|
||||
@ -27,26 +27,27 @@
|
||||
"test": "npm run jest",
|
||||
"test-and-coverage": "npm run jest-and-coverage",
|
||||
"format": "gulp format",
|
||||
"clean": "node ./scripts/clean.js",
|
||||
"clean-all": "node ./scripts/clean-all.js",
|
||||
"clean": "node ./scripts/clean.mjs",
|
||||
"clean-all": "node ./scripts/clean-all.mjs",
|
||||
"cleanall": "npm run clean-all"
|
||||
},
|
||||
"dependencies": {
|
||||
"execa": "5.1.1",
|
||||
"execa": "9.4.0",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-cssnano": "2.1.3",
|
||||
"gulp-rename": "2.0.0",
|
||||
"gulp-replace": "1.1.3",
|
||||
"gulp-replace": "1.1.4",
|
||||
"gulp-terser": "2.1.0",
|
||||
"js-yaml": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/gulp": "4.0.9",
|
||||
"@types/gulp-rename": "2.0.1",
|
||||
"@typescript-eslint/parser": "5.38.0",
|
||||
"@types/gulp": "4.0.17",
|
||||
"@types/gulp-rename": "2.0.6",
|
||||
"@typescript-eslint/parser": "8.8.1",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "10.8.0",
|
||||
"start-server-and-test": "1.14.0",
|
||||
"typescript": "4.8.3"
|
||||
}
|
||||
"cypress": "13.15.0",
|
||||
"start-server-and-test": "2.0.8",
|
||||
"typescript": "5.6.3"
|
||||
},
|
||||
"packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
|
||||
}
|
||||
|
@ -15,9 +15,6 @@
|
||||
"chokidar": "^3.3.1",
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tensorflow/tfjs-node": "3.20.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bull-board/koa": "4.3.1",
|
||||
"@discordapp/twemoji": "14.0.2",
|
||||
@ -38,7 +35,7 @@
|
||||
"aws-sdk": "2.1213.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"blurhash": "1.1.5",
|
||||
"bull": "4.9.0",
|
||||
"bull": "4.16.3",
|
||||
"cacheable-lookup": "6.1.0",
|
||||
"cbor": "8.1.0",
|
||||
"chalk": "5.0.1",
|
||||
@ -81,7 +78,6 @@
|
||||
"nested-property": "4.0.0",
|
||||
"node-fetch": "3.2.10",
|
||||
"nodemailer": "6.7.8",
|
||||
"nsfwjs": "2.4.2",
|
||||
"os-utils": "0.0.14",
|
||||
"parse5": "7.1.1",
|
||||
"pg": "8.8.0",
|
||||
@ -94,7 +90,7 @@
|
||||
"qrcode": "1.5.1",
|
||||
"random-seed": "0.3.0",
|
||||
"ratelimiter": "3.4.1",
|
||||
"re2": "1.17.7",
|
||||
"re2": "1.21.4",
|
||||
"redis-lock": "0.1.4",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rename": "1.0.4",
|
||||
|
@ -1,16 +1,16 @@
|
||||
import * as fs from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname } from 'node:path';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import * as nsfw from 'nsfwjs';
|
||||
import si from 'systeminformation';
|
||||
import type { Config } from '@/config.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import * as fs from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname } from "node:path";
|
||||
import { Inject, Injectable } from "@nestjs/common";
|
||||
import * as nsfw from "nsfwjs";
|
||||
import si from "systeminformation";
|
||||
import type { Config } from "@/config.js";
|
||||
import { DI } from "@/di-symbols.js";
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
|
||||
const REQUIRED_CPU_FLAGS = ['avx2', 'fma'];
|
||||
const REQUIRED_CPU_FLAGS = ["avx2", "fma"];
|
||||
let isSupportedCpu: undefined | boolean = undefined;
|
||||
|
||||
@Injectable()
|
||||
@ -20,27 +20,33 @@ export class AiService {
|
||||
constructor(
|
||||
@Inject(DI.config)
|
||||
private config: Config,
|
||||
) {
|
||||
}
|
||||
) { }
|
||||
|
||||
public async detectSensitive(path: string): Promise<nsfw.predictionType[] | null> {
|
||||
public async detectSensitive(
|
||||
path: string,
|
||||
): Promise<nsfw.predictionType[] | null> {
|
||||
try {
|
||||
if (isSupportedCpu === undefined) {
|
||||
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) {
|
||||
console.error('This CPU cannot use TensorFlow.');
|
||||
console.error("This CPU cannot use TensorFlow.");
|
||||
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 image = await tf.node.decodeImage(buffer, 3) as any;
|
||||
const image = (await tf.node.decodeImage(buffer, 3)) as any;
|
||||
try {
|
||||
const predictions = await this.model.classify(image);
|
||||
return predictions;
|
||||
|
@ -1,248 +1,576 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DI } from '../di-symbols.js';
|
||||
import { AccountUpdateService } from './AccountUpdateService.js';
|
||||
import { AiService } from './AiService.js';
|
||||
import { AntennaService } from './AntennaService.js';
|
||||
import { AppLockService } from './AppLockService.js';
|
||||
import { CaptchaService } from './CaptchaService.js';
|
||||
import { CreateNotificationService } from './CreateNotificationService.js';
|
||||
import { CreateSystemUserService } from './CreateSystemUserService.js';
|
||||
import { CustomEmojiService } from './CustomEmojiService.js';
|
||||
import { DeleteAccountService } from './DeleteAccountService.js';
|
||||
import { DownloadService } from './DownloadService.js';
|
||||
import { DriveService } from './DriveService.js';
|
||||
import { EmailService } from './EmailService.js';
|
||||
import { FederatedInstanceService } from './FederatedInstanceService.js';
|
||||
import { FetchInstanceMetadataService } from './FetchInstanceMetadataService.js';
|
||||
import { GlobalEventService } from './GlobalEventService.js';
|
||||
import { HashtagService } from './HashtagService.js';
|
||||
import { HttpRequestService } from './HttpRequestService.js';
|
||||
import { IdService } from './IdService.js';
|
||||
import { ImageProcessingService } from './ImageProcessingService.js';
|
||||
import { InstanceActorService } from './InstanceActorService.js';
|
||||
import { InternalStorageService } from './InternalStorageService.js';
|
||||
import { MessagingService } from './MessagingService.js';
|
||||
import { MetaService } from './MetaService.js';
|
||||
import { MfmService } from './MfmService.js';
|
||||
import { ModerationLogService } from './ModerationLogService.js';
|
||||
import { NoteCreateService } from './NoteCreateService.js';
|
||||
import { NoteDeleteService } from './NoteDeleteService.js';
|
||||
import { NotePiningService } from './NotePiningService.js';
|
||||
import { NoteReadService } from './NoteReadService.js';
|
||||
import { NotificationService } from './NotificationService.js';
|
||||
import { PollService } from './PollService.js';
|
||||
import { PushNotificationService } from './PushNotificationService.js';
|
||||
import { QueryService } from './QueryService.js';
|
||||
import { ReactionService } from './ReactionService.js';
|
||||
import { RelayService } from './RelayService.js';
|
||||
import { S3Service } from './S3Service.js';
|
||||
import { SignupService } from './SignupService.js';
|
||||
import { TwoFactorAuthenticationService } from './TwoFactorAuthenticationService.js';
|
||||
import { UserBlockingService } from './UserBlockingService.js';
|
||||
import { UserCacheService } from './UserCacheService.js';
|
||||
import { UserFollowingService } from './UserFollowingService.js';
|
||||
import { UserKeypairStoreService } from './UserKeypairStoreService.js';
|
||||
import { UserListService } from './UserListService.js';
|
||||
import { UserMutingService } from './UserMutingService.js';
|
||||
import { UserSuspendService } from './UserSuspendService.js';
|
||||
import { VideoProcessingService } from './VideoProcessingService.js';
|
||||
import { WebhookService } from './WebhookService.js';
|
||||
import { ProxyAccountService } from './ProxyAccountService.js';
|
||||
import { UtilityService } from './UtilityService.js';
|
||||
import { FileInfoService } from './FileInfoService.js';
|
||||
import { ChartLoggerService } from './chart/ChartLoggerService.js';
|
||||
import FederationChart from './chart/charts/federation.js';
|
||||
import NotesChart from './chart/charts/notes.js';
|
||||
import UsersChart from './chart/charts/users.js';
|
||||
import ActiveUsersChart from './chart/charts/active-users.js';
|
||||
import InstanceChart from './chart/charts/instance.js';
|
||||
import PerUserNotesChart from './chart/charts/per-user-notes.js';
|
||||
import DriveChart from './chart/charts/drive.js';
|
||||
import PerUserReactionsChart from './chart/charts/per-user-reactions.js';
|
||||
import HashtagChart from './chart/charts/hashtag.js';
|
||||
import PerUserFollowingChart from './chart/charts/per-user-following.js';
|
||||
import PerUserDriveChart from './chart/charts/per-user-drive.js';
|
||||
import ApRequestChart from './chart/charts/ap-request.js';
|
||||
import { ChartManagementService } from './chart/ChartManagementService.js';
|
||||
import { AbuseUserReportEntityService } from './entities/AbuseUserReportEntityService.js';
|
||||
import { AntennaEntityService } from './entities/AntennaEntityService.js';
|
||||
import { AppEntityService } from './entities/AppEntityService.js';
|
||||
import { AuthSessionEntityService } from './entities/AuthSessionEntityService.js';
|
||||
import { BlockingEntityService } from './entities/BlockingEntityService.js';
|
||||
import { ChannelEntityService } from './entities/ChannelEntityService.js';
|
||||
import { ClipEntityService } from './entities/ClipEntityService.js';
|
||||
import { DriveFileEntityService } from './entities/DriveFileEntityService.js';
|
||||
import { DriveFolderEntityService } from './entities/DriveFolderEntityService.js';
|
||||
import { EmojiEntityService } from './entities/EmojiEntityService.js';
|
||||
import { FollowingEntityService } from './entities/FollowingEntityService.js';
|
||||
import { FollowRequestEntityService } from './entities/FollowRequestEntityService.js';
|
||||
import { GalleryLikeEntityService } from './entities/GalleryLikeEntityService.js';
|
||||
import { GalleryPostEntityService } from './entities/GalleryPostEntityService.js';
|
||||
import { HashtagEntityService } from './entities/HashtagEntityService.js';
|
||||
import { InstanceEntityService } from './entities/InstanceEntityService.js';
|
||||
import { MessagingMessageEntityService } from './entities/MessagingMessageEntityService.js';
|
||||
import { ModerationLogEntityService } from './entities/ModerationLogEntityService.js';
|
||||
import { MutingEntityService } from './entities/MutingEntityService.js';
|
||||
import { NoteEntityService } from './entities/NoteEntityService.js';
|
||||
import { NoteFavoriteEntityService } from './entities/NoteFavoriteEntityService.js';
|
||||
import { NoteReactionEntityService } from './entities/NoteReactionEntityService.js';
|
||||
import { NotificationEntityService } from './entities/NotificationEntityService.js';
|
||||
import { PageEntityService } from './entities/PageEntityService.js';
|
||||
import { PageLikeEntityService } from './entities/PageLikeEntityService.js';
|
||||
import { SigninEntityService } from './entities/SigninEntityService.js';
|
||||
import { UserEntityService } from './entities/UserEntityService.js';
|
||||
import { UserGroupEntityService } from './entities/UserGroupEntityService.js';
|
||||
import { UserGroupInvitationEntityService } from './entities/UserGroupInvitationEntityService.js';
|
||||
import { UserListEntityService } from './entities/UserListEntityService.js';
|
||||
import { ApAudienceService } from './remote/activitypub/ApAudienceService.js';
|
||||
import { ApDbResolverService } from './remote/activitypub/ApDbResolverService.js';
|
||||
import { ApDeliverManagerService } from './remote/activitypub/ApDeliverManagerService.js';
|
||||
import { ApInboxService } from './remote/activitypub/ApInboxService.js';
|
||||
import { ApLoggerService } from './remote/activitypub/ApLoggerService.js';
|
||||
import { ApMfmService } from './remote/activitypub/ApMfmService.js';
|
||||
import { ApRendererService } from './remote/activitypub/ApRendererService.js';
|
||||
import { ApRequestService } from './remote/activitypub/ApRequestService.js';
|
||||
import { ApResolverService } from './remote/activitypub/ApResolverService.js';
|
||||
import { LdSignatureService } from './remote/activitypub/LdSignatureService.js';
|
||||
import { RemoteLoggerService } from './remote/RemoteLoggerService.js';
|
||||
import { ResolveUserService } from './remote/ResolveUserService.js';
|
||||
import { WebfingerService } from './remote/WebfingerService.js';
|
||||
import { ApImageService } from './remote/activitypub/models/ApImageService.js';
|
||||
import { ApMentionService } from './remote/activitypub/models/ApMentionService.js';
|
||||
import { ApNoteService } from './remote/activitypub/models/ApNoteService.js';
|
||||
import { ApPersonService } from './remote/activitypub/models/ApPersonService.js';
|
||||
import { ApQuestionService } from './remote/activitypub/models/ApQuestionService.js';
|
||||
import { QueueModule } from './queue/QueueModule.js';
|
||||
import { QueueService } from './QueueService.js';
|
||||
import { LoggerService } from './LoggerService.js';
|
||||
import type { Provider } from '@nestjs/common';
|
||||
import { Module } from "@nestjs/common";
|
||||
import { DI } from "../di-symbols.js";
|
||||
import { AccountUpdateService } from "./AccountUpdateService.js";
|
||||
// import { AiService } from './AiService.js';
|
||||
import { AntennaService } from "./AntennaService.js";
|
||||
import { AppLockService } from "./AppLockService.js";
|
||||
import { CaptchaService } from "./CaptchaService.js";
|
||||
import { CreateNotificationService } from "./CreateNotificationService.js";
|
||||
import { CreateSystemUserService } from "./CreateSystemUserService.js";
|
||||
import { CustomEmojiService } from "./CustomEmojiService.js";
|
||||
import { DeleteAccountService } from "./DeleteAccountService.js";
|
||||
import { DownloadService } from "./DownloadService.js";
|
||||
import { DriveService } from "./DriveService.js";
|
||||
import { EmailService } from "./EmailService.js";
|
||||
import { FederatedInstanceService } from "./FederatedInstanceService.js";
|
||||
import { FetchInstanceMetadataService } from "./FetchInstanceMetadataService.js";
|
||||
import { GlobalEventService } from "./GlobalEventService.js";
|
||||
import { HashtagService } from "./HashtagService.js";
|
||||
import { HttpRequestService } from "./HttpRequestService.js";
|
||||
import { IdService } from "./IdService.js";
|
||||
import { ImageProcessingService } from "./ImageProcessingService.js";
|
||||
import { InstanceActorService } from "./InstanceActorService.js";
|
||||
import { InternalStorageService } from "./InternalStorageService.js";
|
||||
import { MessagingService } from "./MessagingService.js";
|
||||
import { MetaService } from "./MetaService.js";
|
||||
import { MfmService } from "./MfmService.js";
|
||||
import { ModerationLogService } from "./ModerationLogService.js";
|
||||
import { NoteCreateService } from "./NoteCreateService.js";
|
||||
import { NoteDeleteService } from "./NoteDeleteService.js";
|
||||
import { NotePiningService } from "./NotePiningService.js";
|
||||
import { NoteReadService } from "./NoteReadService.js";
|
||||
import { NotificationService } from "./NotificationService.js";
|
||||
import { PollService } from "./PollService.js";
|
||||
import { PushNotificationService } from "./PushNotificationService.js";
|
||||
import { QueryService } from "./QueryService.js";
|
||||
import { ReactionService } from "./ReactionService.js";
|
||||
import { RelayService } from "./RelayService.js";
|
||||
import { S3Service } from "./S3Service.js";
|
||||
import { SignupService } from "./SignupService.js";
|
||||
import { TwoFactorAuthenticationService } from "./TwoFactorAuthenticationService.js";
|
||||
import { UserBlockingService } from "./UserBlockingService.js";
|
||||
import { UserCacheService } from "./UserCacheService.js";
|
||||
import { UserFollowingService } from "./UserFollowingService.js";
|
||||
import { UserKeypairStoreService } from "./UserKeypairStoreService.js";
|
||||
import { UserListService } from "./UserListService.js";
|
||||
import { UserMutingService } from "./UserMutingService.js";
|
||||
import { UserSuspendService } from "./UserSuspendService.js";
|
||||
import { VideoProcessingService } from "./VideoProcessingService.js";
|
||||
import { WebhookService } from "./WebhookService.js";
|
||||
import { ProxyAccountService } from "./ProxyAccountService.js";
|
||||
import { UtilityService } from "./UtilityService.js";
|
||||
import { FileInfoService } from "./FileInfoService.js";
|
||||
import { ChartLoggerService } from "./chart/ChartLoggerService.js";
|
||||
import FederationChart from "./chart/charts/federation.js";
|
||||
import NotesChart from "./chart/charts/notes.js";
|
||||
import UsersChart from "./chart/charts/users.js";
|
||||
import ActiveUsersChart from "./chart/charts/active-users.js";
|
||||
import InstanceChart from "./chart/charts/instance.js";
|
||||
import PerUserNotesChart from "./chart/charts/per-user-notes.js";
|
||||
import DriveChart from "./chart/charts/drive.js";
|
||||
import PerUserReactionsChart from "./chart/charts/per-user-reactions.js";
|
||||
import HashtagChart from "./chart/charts/hashtag.js";
|
||||
import PerUserFollowingChart from "./chart/charts/per-user-following.js";
|
||||
import PerUserDriveChart from "./chart/charts/per-user-drive.js";
|
||||
import ApRequestChart from "./chart/charts/ap-request.js";
|
||||
import { ChartManagementService } from "./chart/ChartManagementService.js";
|
||||
import { AbuseUserReportEntityService } from "./entities/AbuseUserReportEntityService.js";
|
||||
import { AntennaEntityService } from "./entities/AntennaEntityService.js";
|
||||
import { AppEntityService } from "./entities/AppEntityService.js";
|
||||
import { AuthSessionEntityService } from "./entities/AuthSessionEntityService.js";
|
||||
import { BlockingEntityService } from "./entities/BlockingEntityService.js";
|
||||
import { ChannelEntityService } from "./entities/ChannelEntityService.js";
|
||||
import { ClipEntityService } from "./entities/ClipEntityService.js";
|
||||
import { DriveFileEntityService } from "./entities/DriveFileEntityService.js";
|
||||
import { DriveFolderEntityService } from "./entities/DriveFolderEntityService.js";
|
||||
import { EmojiEntityService } from "./entities/EmojiEntityService.js";
|
||||
import { FollowingEntityService } from "./entities/FollowingEntityService.js";
|
||||
import { FollowRequestEntityService } from "./entities/FollowRequestEntityService.js";
|
||||
import { GalleryLikeEntityService } from "./entities/GalleryLikeEntityService.js";
|
||||
import { GalleryPostEntityService } from "./entities/GalleryPostEntityService.js";
|
||||
import { HashtagEntityService } from "./entities/HashtagEntityService.js";
|
||||
import { InstanceEntityService } from "./entities/InstanceEntityService.js";
|
||||
import { MessagingMessageEntityService } from "./entities/MessagingMessageEntityService.js";
|
||||
import { ModerationLogEntityService } from "./entities/ModerationLogEntityService.js";
|
||||
import { MutingEntityService } from "./entities/MutingEntityService.js";
|
||||
import { NoteEntityService } from "./entities/NoteEntityService.js";
|
||||
import { NoteFavoriteEntityService } from "./entities/NoteFavoriteEntityService.js";
|
||||
import { NoteReactionEntityService } from "./entities/NoteReactionEntityService.js";
|
||||
import { NotificationEntityService } from "./entities/NotificationEntityService.js";
|
||||
import { PageEntityService } from "./entities/PageEntityService.js";
|
||||
import { PageLikeEntityService } from "./entities/PageLikeEntityService.js";
|
||||
import { SigninEntityService } from "./entities/SigninEntityService.js";
|
||||
import { UserEntityService } from "./entities/UserEntityService.js";
|
||||
import { UserGroupEntityService } from "./entities/UserGroupEntityService.js";
|
||||
import { UserGroupInvitationEntityService } from "./entities/UserGroupInvitationEntityService.js";
|
||||
import { UserListEntityService } from "./entities/UserListEntityService.js";
|
||||
import { ApAudienceService } from "./remote/activitypub/ApAudienceService.js";
|
||||
import { ApDbResolverService } from "./remote/activitypub/ApDbResolverService.js";
|
||||
import { ApDeliverManagerService } from "./remote/activitypub/ApDeliverManagerService.js";
|
||||
import { ApInboxService } from "./remote/activitypub/ApInboxService.js";
|
||||
import { ApLoggerService } from "./remote/activitypub/ApLoggerService.js";
|
||||
import { ApMfmService } from "./remote/activitypub/ApMfmService.js";
|
||||
import { ApRendererService } from "./remote/activitypub/ApRendererService.js";
|
||||
import { ApRequestService } from "./remote/activitypub/ApRequestService.js";
|
||||
import { ApResolverService } from "./remote/activitypub/ApResolverService.js";
|
||||
import { LdSignatureService } from "./remote/activitypub/LdSignatureService.js";
|
||||
import { RemoteLoggerService } from "./remote/RemoteLoggerService.js";
|
||||
import { ResolveUserService } from "./remote/ResolveUserService.js";
|
||||
import { WebfingerService } from "./remote/WebfingerService.js";
|
||||
import { ApImageService } from "./remote/activitypub/models/ApImageService.js";
|
||||
import { ApMentionService } from "./remote/activitypub/models/ApMentionService.js";
|
||||
import { ApNoteService } from "./remote/activitypub/models/ApNoteService.js";
|
||||
import { ApPersonService } from "./remote/activitypub/models/ApPersonService.js";
|
||||
import { ApQuestionService } from "./remote/activitypub/models/ApQuestionService.js";
|
||||
import { QueueModule } from "./queue/QueueModule.js";
|
||||
import { QueueService } from "./QueueService.js";
|
||||
import { LoggerService } from "./LoggerService.js";
|
||||
import type { Provider } from "@nestjs/common";
|
||||
|
||||
//#region 文字列ベースでのinjection用(循環参照対応のため)
|
||||
const $LoggerService: Provider = { provide: 'LoggerService', useExisting: LoggerService };
|
||||
const $AccountUpdateService: Provider = { provide: 'AccountUpdateService', useExisting: AccountUpdateService };
|
||||
const $AiService: Provider = { provide: 'AiService', useExisting: AiService };
|
||||
const $AntennaService: Provider = { provide: 'AntennaService', useExisting: AntennaService };
|
||||
const $AppLockService: Provider = { provide: 'AppLockService', useExisting: AppLockService };
|
||||
const $CaptchaService: Provider = { provide: 'CaptchaService', useExisting: CaptchaService };
|
||||
const $CreateNotificationService: Provider = { provide: 'CreateNotificationService', useExisting: CreateNotificationService };
|
||||
const $CreateSystemUserService: Provider = { provide: 'CreateSystemUserService', useExisting: CreateSystemUserService };
|
||||
const $CustomEmojiService: Provider = { provide: 'CustomEmojiService', useExisting: CustomEmojiService };
|
||||
const $DeleteAccountService: Provider = { provide: 'DeleteAccountService', useExisting: DeleteAccountService };
|
||||
const $DownloadService: Provider = { provide: 'DownloadService', useExisting: DownloadService };
|
||||
const $DriveService: Provider = { provide: 'DriveService', useExisting: DriveService };
|
||||
const $EmailService: Provider = { provide: 'EmailService', useExisting: EmailService };
|
||||
const $FederatedInstanceService: Provider = { provide: 'FederatedInstanceService', useExisting: FederatedInstanceService };
|
||||
const $FetchInstanceMetadataService: Provider = { provide: 'FetchInstanceMetadataService', useExisting: FetchInstanceMetadataService };
|
||||
const $GlobalEventService: Provider = { provide: 'GlobalEventService', useExisting: GlobalEventService };
|
||||
const $HashtagService: Provider = { provide: 'HashtagService', useExisting: HashtagService };
|
||||
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 $LoggerService: Provider = {
|
||||
provide: "LoggerService",
|
||||
useExisting: LoggerService,
|
||||
};
|
||||
const $AccountUpdateService: Provider = {
|
||||
provide: "AccountUpdateService",
|
||||
useExisting: AccountUpdateService,
|
||||
};
|
||||
// const $AiService: Provider = { provide: "AiService", useExisting: AiService };
|
||||
const $AntennaService: Provider = {
|
||||
provide: "AntennaService",
|
||||
useExisting: AntennaService,
|
||||
};
|
||||
const $AppLockService: Provider = {
|
||||
provide: "AppLockService",
|
||||
useExisting: AppLockService,
|
||||
};
|
||||
const $CaptchaService: Provider = {
|
||||
provide: "CaptchaService",
|
||||
useExisting: CaptchaService,
|
||||
};
|
||||
const $CreateNotificationService: Provider = {
|
||||
provide: "CreateNotificationService",
|
||||
useExisting: CreateNotificationService,
|
||||
};
|
||||
const $CreateSystemUserService: Provider = {
|
||||
provide: "CreateSystemUserService",
|
||||
useExisting: CreateSystemUserService,
|
||||
};
|
||||
const $CustomEmojiService: Provider = {
|
||||
provide: "CustomEmojiService",
|
||||
useExisting: CustomEmojiService,
|
||||
};
|
||||
const $DeleteAccountService: Provider = {
|
||||
provide: "DeleteAccountService",
|
||||
useExisting: DeleteAccountService,
|
||||
};
|
||||
const $DownloadService: Provider = {
|
||||
provide: "DownloadService",
|
||||
useExisting: DownloadService,
|
||||
};
|
||||
const $DriveService: Provider = {
|
||||
provide: "DriveService",
|
||||
useExisting: DriveService,
|
||||
};
|
||||
const $EmailService: Provider = {
|
||||
provide: "EmailService",
|
||||
useExisting: EmailService,
|
||||
};
|
||||
const $FederatedInstanceService: Provider = {
|
||||
provide: "FederatedInstanceService",
|
||||
useExisting: FederatedInstanceService,
|
||||
};
|
||||
const $FetchInstanceMetadataService: Provider = {
|
||||
provide: "FetchInstanceMetadataService",
|
||||
useExisting: FetchInstanceMetadataService,
|
||||
};
|
||||
const $GlobalEventService: Provider = {
|
||||
provide: "GlobalEventService",
|
||||
useExisting: GlobalEventService,
|
||||
};
|
||||
const $HashtagService: Provider = {
|
||||
provide: "HashtagService",
|
||||
useExisting: HashtagService,
|
||||
};
|
||||
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 $AntennaEntityService: Provider = { provide: 'AntennaEntityService', useExisting: AntennaEntityService };
|
||||
const $AppEntityService: Provider = { provide: 'AppEntityService', useExisting: AppEntityService };
|
||||
const $AuthSessionEntityService: Provider = { provide: 'AuthSessionEntityService', useExisting: AuthSessionEntityService };
|
||||
const $BlockingEntityService: Provider = { provide: 'BlockingEntityService', useExisting: BlockingEntityService };
|
||||
const $ChannelEntityService: Provider = { provide: 'ChannelEntityService', useExisting: ChannelEntityService };
|
||||
const $ClipEntityService: Provider = { provide: 'ClipEntityService', useExisting: ClipEntityService };
|
||||
const $DriveFileEntityService: Provider = { 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 $AbuseUserReportEntityService: Provider = {
|
||||
provide: "AbuseUserReportEntityService",
|
||||
useExisting: AbuseUserReportEntityService,
|
||||
};
|
||||
const $AntennaEntityService: Provider = {
|
||||
provide: "AntennaEntityService",
|
||||
useExisting: AntennaEntityService,
|
||||
};
|
||||
const $AppEntityService: Provider = {
|
||||
provide: "AppEntityService",
|
||||
useExisting: AppEntityService,
|
||||
};
|
||||
const $AuthSessionEntityService: Provider = {
|
||||
provide: "AuthSessionEntityService",
|
||||
useExisting: AuthSessionEntityService,
|
||||
};
|
||||
const $BlockingEntityService: Provider = {
|
||||
provide: "BlockingEntityService",
|
||||
useExisting: BlockingEntityService,
|
||||
};
|
||||
const $ChannelEntityService: Provider = {
|
||||
provide: "ChannelEntityService",
|
||||
useExisting: ChannelEntityService,
|
||||
};
|
||||
const $ClipEntityService: Provider = {
|
||||
provide: "ClipEntityService",
|
||||
useExisting: ClipEntityService,
|
||||
};
|
||||
const $DriveFileEntityService: Provider = {
|
||||
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 $ApDbResolverService: Provider = { provide: 'ApDbResolverService', useExisting: ApDbResolverService };
|
||||
const $ApDeliverManagerService: Provider = { provide: 'ApDeliverManagerService', useExisting: ApDeliverManagerService };
|
||||
const $ApInboxService: Provider = { provide: 'ApInboxService', useExisting: ApInboxService };
|
||||
const $ApLoggerService: Provider = { 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 };
|
||||
const $ApAudienceService: Provider = {
|
||||
provide: "ApAudienceService",
|
||||
useExisting: ApAudienceService,
|
||||
};
|
||||
const $ApDbResolverService: Provider = {
|
||||
provide: "ApDbResolverService",
|
||||
useExisting: ApDbResolverService,
|
||||
};
|
||||
const $ApDeliverManagerService: Provider = {
|
||||
provide: "ApDeliverManagerService",
|
||||
useExisting: ApDeliverManagerService,
|
||||
};
|
||||
const $ApInboxService: Provider = {
|
||||
provide: "ApInboxService",
|
||||
useExisting: ApInboxService,
|
||||
};
|
||||
const $ApLoggerService: Provider = {
|
||||
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
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
QueueModule,
|
||||
],
|
||||
imports: [QueueModule],
|
||||
providers: [
|
||||
LoggerService,
|
||||
AccountUpdateService,
|
||||
AiService,
|
||||
// AiService,
|
||||
AntennaService,
|
||||
AppLockService,
|
||||
CaptchaService,
|
||||
@ -358,7 +686,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||
//#region 文字列ベースでのinjection用(循環参照対応のため)
|
||||
$LoggerService,
|
||||
$AccountUpdateService,
|
||||
$AiService,
|
||||
// $AiService,
|
||||
$AntennaService,
|
||||
$AppLockService,
|
||||
$CaptchaService,
|
||||
@ -475,7 +803,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||
QueueModule,
|
||||
LoggerService,
|
||||
AccountUpdateService,
|
||||
AiService,
|
||||
// AiService,
|
||||
AntennaService,
|
||||
AppLockService,
|
||||
CaptchaService,
|
||||
@ -590,7 +918,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||
//#region 文字列ベースでのinjection用(循環参照対応のため)
|
||||
$LoggerService,
|
||||
$AccountUpdateService,
|
||||
$AiService,
|
||||
// $AiService,
|
||||
$AntennaService,
|
||||
$AppLockService,
|
||||
$CaptchaService,
|
||||
@ -703,4 +1031,4 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||
//#endregion
|
||||
],
|
||||
})
|
||||
export class CoreModule {}
|
||||
export class CoreModule { }
|
||||
|
@ -1,19 +1,19 @@
|
||||
import * as fs from 'node:fs';
|
||||
import * as crypto from 'node:crypto';
|
||||
import { join } from 'node:path';
|
||||
import * as stream from 'node:stream';
|
||||
import * as util from 'node:util';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { FSWatcher } from 'chokidar';
|
||||
import { fileTypeFromFile } from 'file-type';
|
||||
import FFmpeg from 'fluent-ffmpeg';
|
||||
import isSvg from 'is-svg';
|
||||
import probeImageSize from 'probe-image-size';
|
||||
import { type predictionType } from 'nsfwjs';
|
||||
import sharp from 'sharp';
|
||||
import { encode } from 'blurhash';
|
||||
import { createTempDir } from '@/misc/create-temp.js';
|
||||
import { AiService } from '@/core/AiService.js';
|
||||
import * as fs from "node:fs";
|
||||
import * as crypto from "node:crypto";
|
||||
import { join } from "node:path";
|
||||
import * as stream from "node:stream";
|
||||
import * as util from "node:util";
|
||||
import { Inject, Injectable } from "@nestjs/common";
|
||||
import { FSWatcher } from "chokidar";
|
||||
import { fileTypeFromFile } from "file-type";
|
||||
import FFmpeg from "fluent-ffmpeg";
|
||||
import isSvg from "is-svg";
|
||||
import probeImageSize from "probe-image-size";
|
||||
import { type predictionType } from "nsfwjs";
|
||||
import sharp from "sharp";
|
||||
import { encode } from "blurhash";
|
||||
import { createTempDir } from "@/misc/create-temp.js";
|
||||
// import { AiService } from '@/core/AiService.js';
|
||||
|
||||
const pipeline = util.promisify(stream.pipeline);
|
||||
|
||||
@ -34,30 +34,30 @@ export type FileInfo = {
|
||||
};
|
||||
|
||||
const TYPE_OCTET_STREAM = {
|
||||
mime: 'application/octet-stream',
|
||||
mime: "application/octet-stream",
|
||||
ext: null,
|
||||
};
|
||||
|
||||
const TYPE_SVG = {
|
||||
mime: 'image/svg+xml',
|
||||
ext: 'svg',
|
||||
mime: "image/svg+xml",
|
||||
ext: "svg",
|
||||
};
|
||||
@Injectable()
|
||||
export class FileInfoService {
|
||||
constructor(
|
||||
private aiService: AiService,
|
||||
) {
|
||||
}
|
||||
constructor() {} // private aiService: AiService,
|
||||
|
||||
/**
|
||||
* Get file information
|
||||
*/
|
||||
public async getFileInfo(path: string, opts: {
|
||||
skipSensitiveDetection: boolean;
|
||||
sensitiveThreshold?: number;
|
||||
sensitiveThresholdForPorn?: number;
|
||||
enableSensitiveMediaDetectionForVideos?: boolean;
|
||||
}): Promise<FileInfo> {
|
||||
public async getFileInfo(
|
||||
path: string,
|
||||
opts: {
|
||||
skipSensitiveDetection: boolean;
|
||||
sensitiveThreshold?: number;
|
||||
sensitiveThresholdForPorn?: number;
|
||||
enableSensitiveMediaDetectionForVideos?: boolean;
|
||||
},
|
||||
): Promise<FileInfo> {
|
||||
const warnings = [] as string[];
|
||||
|
||||
const size = await this.getFileSize(path);
|
||||
@ -70,24 +70,36 @@ export class FileInfoService {
|
||||
let height: 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)) {
|
||||
const imageSize = await this.detectImageSize(path).catch(e => {
|
||||
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)
|
||||
) {
|
||||
const imageSize = await this.detectImageSize(path).catch((e) => {
|
||||
warnings.push(`detectImageSize failed: ${e}`);
|
||||
return undefined;
|
||||
});
|
||||
|
||||
// うまく判定できない画像は octet-stream にする
|
||||
if (!imageSize) {
|
||||
warnings.push('cannot detect image dimensions');
|
||||
warnings.push("cannot detect image dimensions");
|
||||
type = TYPE_OCTET_STREAM;
|
||||
} else if (imageSize.wUnits === 'px') {
|
||||
} else if (imageSize.wUnits === "px") {
|
||||
width = imageSize.width;
|
||||
height = imageSize.height;
|
||||
orientation = imageSize.orientation;
|
||||
|
||||
// 制限を超えている画像は octet-stream にする
|
||||
if (imageSize.width > 16383 || imageSize.height > 16383) {
|
||||
warnings.push('image dimensions exceeds limits');
|
||||
warnings.push("image dimensions exceeds limits");
|
||||
type = TYPE_OCTET_STREAM;
|
||||
}
|
||||
} else {
|
||||
@ -97,8 +109,17 @@ export class FileInfoService {
|
||||
|
||||
let blurhash: string | undefined;
|
||||
|
||||
if (['image/jpeg', 'image/gif', 'image/png', 'image/apng', 'image/webp', 'image/svg+xml'].includes(type.mime)) {
|
||||
blurhash = await this.getBlurhash(path).catch(e => {
|
||||
if (
|
||||
[
|
||||
"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}`);
|
||||
return undefined;
|
||||
});
|
||||
@ -114,11 +135,14 @@ export class FileInfoService {
|
||||
opts.sensitiveThreshold ?? 0.5,
|
||||
opts.sensitiveThresholdForPorn ?? 0.75,
|
||||
opts.enableSensitiveMediaDetectionForVideos ?? false,
|
||||
).then(value => {
|
||||
[sensitive, porn] = value;
|
||||
}, error => {
|
||||
warnings.push(`detectSensitivity failed: ${error}`);
|
||||
});
|
||||
).then(
|
||||
(value) => {
|
||||
[sensitive, porn] = value;
|
||||
},
|
||||
(error) => {
|
||||
warnings.push(`detectSensitivity failed: ${error}`);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
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 porn = false;
|
||||
|
||||
function judgePrediction(result: readonly predictionType[]): [sensitive: boolean, porn: boolean] {
|
||||
function judgePrediction(
|
||||
result: readonly predictionType[],
|
||||
): [sensitive: boolean, porn: boolean] {
|
||||
let sensitive = false;
|
||||
let porn = false;
|
||||
|
||||
if ((result.find(x => x.className === 'Sexy')?.probability ?? 0) > sensitiveThreshold) 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 === "Sexy")?.probability ?? 0) >
|
||||
sensitiveThreshold
|
||||
)
|
||||
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;
|
||||
if (
|
||||
(result.find((x) => x.className === "Porn")?.probability ?? 0) >
|
||||
sensitiveThresholdForPorn
|
||||
)
|
||||
porn = true;
|
||||
|
||||
return [sensitive, porn];
|
||||
}
|
||||
|
||||
if (['image/jpeg', 'image/png', 'image/webp'].includes(mime)) {
|
||||
const result = await this.aiService.detectSensitive(source);
|
||||
if (result) {
|
||||
[sensitive, porn] = judgePrediction(result);
|
||||
}
|
||||
} else if (analyzeVideo && (mime === 'image/apng' || mime.startsWith('video/'))) {
|
||||
if (["image/jpeg", "image/png", "image/webp"].includes(mime)) {
|
||||
// const result = await this.aiService.detectSensitive(source);
|
||||
// if (result) {
|
||||
// [sensitive, porn] = judgePrediction(result);
|
||||
// }
|
||||
} else if (
|
||||
analyzeVideo &&
|
||||
(mime === "image/apng" || mime.startsWith("video/"))
|
||||
) {
|
||||
const [outDir, disposeOutDir] = await createTempDir();
|
||||
try {
|
||||
const command = FFmpeg()
|
||||
.input(source)
|
||||
.inputOptions([
|
||||
'-skip_frame', 'nokey', // 可能ならキーフレームのみを取得してほしいとする(そうなるとは限らない)
|
||||
'-lowres', '3', // 元の画質でデコードする必要はないので 1/8 画質でデコードしてもよいとする(そうなるとは限らない)
|
||||
"-skip_frame",
|
||||
"nokey", // 可能ならキーフレームのみを取得してほしいとする(そうなるとは限らない)
|
||||
"-lowres",
|
||||
"3", // 元の画質でデコードする必要はないので 1/8 画質でデコードしてもよいとする(そうなるとは限らない)
|
||||
])
|
||||
.noAudio()
|
||||
.videoFilters([
|
||||
{
|
||||
filter: 'select', // フレームのフィルタリング
|
||||
filter: "select", // フレームのフィルタリング
|
||||
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: {
|
||||
amount: '0', // 暗さに関わらず全てのフレームで測定値を取る
|
||||
amount: "0", // 暗さに関わらず全てのフレームで測定値を取る
|
||||
},
|
||||
},
|
||||
{
|
||||
filter: 'metadata',
|
||||
filter: "metadata",
|
||||
options: {
|
||||
mode: 'select', // フレーム選択モード
|
||||
key: 'lavfi.blackframe.pblack', // フレームにおける暗部の百分率(前のフィルタからのメタデータを参照する)
|
||||
value: '50',
|
||||
function: 'less', // 50% 未満のフレームを選択する(50% 以上暗部があるフレームだと誤検知を招くかもしれないので)
|
||||
mode: "select", // フレーム選択モード
|
||||
key: "lavfi.blackframe.pblack", // フレームにおける暗部の百分率(前のフィルタからのメタデータを参照する)
|
||||
value: "50",
|
||||
function: "less", // 50% 未満のフレームを選択する(50% 以上暗部があるフレームだと誤検知を招くかもしれないので)
|
||||
},
|
||||
},
|
||||
{
|
||||
filter: 'scale',
|
||||
filter: "scale",
|
||||
options: {
|
||||
w: 299,
|
||||
h: 299,
|
||||
},
|
||||
},
|
||||
])
|
||||
.format('image2')
|
||||
.output(join(outDir, '%d.png'))
|
||||
.outputOptions(['-vsync', '0']); // 可変フレームレートにすることで穴埋めをさせない
|
||||
.format("image2")
|
||||
.output(join(outDir, "%d.png"))
|
||||
.outputOptions(["-vsync", "0"]); // 可変フレームレートにすることで穴埋めをさせない
|
||||
const results: ReturnType<typeof judgePrediction>[] = [];
|
||||
let frameIndex = 0;
|
||||
let targetIndex = 0;
|
||||
@ -212,16 +265,20 @@ export class FileInfoService {
|
||||
}
|
||||
targetIndex = nextIndex;
|
||||
nextIndex += index; // fibonacci sequence によってフレーム数制限を掛ける
|
||||
const result = await this.aiService.detectSensitive(path);
|
||||
if (result) {
|
||||
results.push(judgePrediction(result));
|
||||
}
|
||||
// const result = await this.aiService.detectSensitive(path);
|
||||
// if (result) {
|
||||
// results.push(judgePrediction(result));
|
||||
// }
|
||||
} finally {
|
||||
fs.promises.unlink(path);
|
||||
}
|
||||
}
|
||||
sensitive = results.filter(x => x[0]).length >= Math.ceil(results.length * sensitiveThreshold);
|
||||
porn = results.filter(x => x[1]).length >= Math.ceil(results.length * sensitiveThresholdForPorn);
|
||||
sensitive =
|
||||
results.filter((x) => x[0]).length >=
|
||||
Math.ceil(results.length * sensitiveThreshold);
|
||||
porn =
|
||||
results.filter((x) => x[1]).length >=
|
||||
Math.ceil(results.length * sensitiveThresholdForPorn);
|
||||
} finally {
|
||||
disposeOutDir();
|
||||
}
|
||||
@ -230,35 +287,41 @@ export class FileInfoService {
|
||||
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({
|
||||
cwd,
|
||||
disableGlobbing: true,
|
||||
});
|
||||
let finished = false;
|
||||
command.once('end', () => {
|
||||
command.once("end", () => {
|
||||
finished = true;
|
||||
watcher.close();
|
||||
});
|
||||
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 next = `${i + 1}.png`;
|
||||
const framePath = join(cwd, current);
|
||||
if (await this.exists(join(cwd, next))) {
|
||||
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);
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
watcher.on('add', function onAdd(path) {
|
||||
if (path === next) { // 次フレームの書き出しが始まっているなら、現在フレームの書き出しは終わっている
|
||||
watcher.on("add", function onAdd(path) {
|
||||
if (path === next) {
|
||||
// 次フレームの書き出しが始まっているなら、現在フレームの書き出しは終わっている
|
||||
watcher.unwatch(current);
|
||||
watcher.off('add', onAdd);
|
||||
watcher.off("add", onAdd);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
command.once('end', resolve); // 全てのフレームを処理し終わったなら、最終フレームである現在フレームの書き出しは終わっている
|
||||
command.once('error', reject);
|
||||
command.once("end", resolve); // 全てのフレームを処理し終わったなら、最終フレームである現在フレームの書き出しは終わっている
|
||||
command.once("error", reject);
|
||||
});
|
||||
yield framePath;
|
||||
} else if (await this.exists(framePath)) {
|
||||
@ -270,17 +333,20 @@ export class FileInfoService {
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
public async detectType(path: string): Promise<{
|
||||
mime: string;
|
||||
ext: string | null;
|
||||
}> {
|
||||
// Check 0 byte
|
||||
mime: string;
|
||||
ext: string | null;
|
||||
}> {
|
||||
// Check 0 byte
|
||||
const fileSize = await this.getFileSize(path);
|
||||
if (fileSize === 0) {
|
||||
return TYPE_OCTET_STREAM;
|
||||
@ -289,8 +355,8 @@ export class FileInfoService {
|
||||
const type = await fileTypeFromFile(path);
|
||||
|
||||
if (type) {
|
||||
// XMLはSVGかもしれない
|
||||
if (type.mime === 'application/xml' && await this.checkSvg(path)) {
|
||||
// XMLはSVGかもしれない
|
||||
if (type.mime === "application/xml" && (await this.checkSvg(path))) {
|
||||
return TYPE_SVG;
|
||||
}
|
||||
|
||||
@ -334,7 +400,7 @@ export class FileInfoService {
|
||||
* Calculate MD5 hash
|
||||
*/
|
||||
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);
|
||||
return hash.read();
|
||||
}
|
||||
@ -343,12 +409,12 @@ export class FileInfoService {
|
||||
* Detect dimensions of image
|
||||
*/
|
||||
private async detectImageSize(path: string): Promise<{
|
||||
width: number;
|
||||
height: number;
|
||||
wUnits: string;
|
||||
hUnits: string;
|
||||
orientation?: number;
|
||||
}> {
|
||||
width: number;
|
||||
height: number;
|
||||
wUnits: string;
|
||||
hUnits: string;
|
||||
orientation?: number;
|
||||
}> {
|
||||
const readable = fs.createReadStream(path);
|
||||
const imageSize = await probeImageSize(readable);
|
||||
readable.destroy();
|
||||
@ -363,7 +429,7 @@ export class FileInfoService {
|
||||
sharp(path)
|
||||
.raw()
|
||||
.ensureAlpha()
|
||||
.resize(64, 64, { fit: 'inside' })
|
||||
.resize(64, 64, { fit: "inside" })
|
||||
.toBuffer((err, buffer, { width, height }) => {
|
||||
if (err) return reject(err);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,87 +1,98 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
'node': false,
|
||||
node: false,
|
||||
},
|
||||
parser: 'vue-eslint-parser',
|
||||
parser: "vue-eslint-parser",
|
||||
parserOptions: {
|
||||
'parser': '@typescript-eslint/parser',
|
||||
parser: "@typescript-eslint/parser",
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
extraFileExtensions: ['.vue'],
|
||||
project: ["./tsconfig.json"],
|
||||
extraFileExtensions: [".vue"],
|
||||
},
|
||||
extends: [
|
||||
'../shared/.eslintrc.js',
|
||||
'plugin:vue/vue3-recommended',
|
||||
],
|
||||
extends: ["../shared/.eslintrc.js", "plugin:vue/vue3-recommended"],
|
||||
rules: {
|
||||
'@typescript-eslint/no-empty-interface': [
|
||||
'error',
|
||||
"@typescript-eslint/no-empty-interface": [
|
||||
"error",
|
||||
{
|
||||
'allowSingleExtends': true,
|
||||
allowSingleExtends: true,
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/prefer-nullish-coalescing': [
|
||||
'error',
|
||||
],
|
||||
"@typescript-eslint/prefer-nullish-coalescing": ["error"],
|
||||
// window の禁止理由: グローバルスコープと衝突し、予期せぬ結果を招くため
|
||||
// e の禁止理由: error や event など、複数のキーワードの頭文字であり分かりにくいため
|
||||
'id-denylist': ['error', 'window', 'e'],
|
||||
'no-shadow': ['warn'],
|
||||
'vue/attributes-order': ['error', {
|
||||
'alphabetical': false,
|
||||
}],
|
||||
'vue/no-use-v-if-with-v-for': ['error', {
|
||||
'allowUsingIterationVar': false,
|
||||
}],
|
||||
'vue/no-ref-as-operand': 'error',
|
||||
'vue/no-multi-spaces': ['error', {
|
||||
'ignoreProperties': false,
|
||||
}],
|
||||
'vue/no-v-html': 'warn',
|
||||
'vue/order-in-components': 'error',
|
||||
'vue/html-indent': ['warn', 'tab', {
|
||||
'attribute': 1,
|
||||
'baseIndent': 0,
|
||||
'closeBracket': 0,
|
||||
'alignAttributesVertically': true,
|
||||
'ignores': [],
|
||||
}],
|
||||
'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',
|
||||
"id-denylist": ["error", "window", "e"],
|
||||
"no-shadow": ["warn"],
|
||||
"vue/attributes-order": [
|
||||
"error",
|
||||
{
|
||||
alphabetical: false,
|
||||
},
|
||||
],
|
||||
"vue/no-use-v-if-with-v-for": [
|
||||
"error",
|
||||
{
|
||||
allowUsingIterationVar: false,
|
||||
},
|
||||
],
|
||||
"vue/no-ref-as-operand": "error",
|
||||
"vue/no-multi-spaces": [
|
||||
"error",
|
||||
{
|
||||
ignoreProperties: false,
|
||||
},
|
||||
],
|
||||
"vue/no-v-html": "warn",
|
||||
"vue/order-in-components": "error",
|
||||
"vue/html-indent": [
|
||||
"warn",
|
||||
"tab",
|
||||
{
|
||||
attribute: 1,
|
||||
baseIndent: 0,
|
||||
closeBracket: 0,
|
||||
alignAttributesVertically: true,
|
||||
ignores: [],
|
||||
},
|
||||
],
|
||||
"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: {
|
||||
// Node.js
|
||||
'module': false,
|
||||
'require': false,
|
||||
'__dirname': false,
|
||||
module: false,
|
||||
require: false,
|
||||
__dirname: false,
|
||||
|
||||
// Vue
|
||||
'$$': false,
|
||||
'$ref': false,
|
||||
'$shallowRef': false,
|
||||
'$computed': false,
|
||||
// '$$': false,
|
||||
// '$ref': false,
|
||||
// '$shallowRef': false,
|
||||
// '$computed': false,
|
||||
|
||||
// Misskey
|
||||
'_DEV_': false,
|
||||
'_LANGS_': false,
|
||||
'_VERSION_': false,
|
||||
'_ENV_': false,
|
||||
'_PERF_PREFIX_': false,
|
||||
'_DATA_TRANSFER_DRIVE_FILE_': false,
|
||||
'_DATA_TRANSFER_DRIVE_FOLDER_': false,
|
||||
'_DATA_TRANSFER_DECK_COLUMN_': false,
|
||||
_DEV_: false,
|
||||
_LANGS_: false,
|
||||
_VERSION_: false,
|
||||
_ENV_: false,
|
||||
_PERF_PREFIX_: false,
|
||||
_DATA_TRANSFER_DRIVE_FILE_: false,
|
||||
_DATA_TRANSFER_DRIVE_FOLDER_: false,
|
||||
_DATA_TRANSFER_DECK_COLUMN_: false,
|
||||
},
|
||||
};
|
||||
|
@ -3,88 +3,92 @@
|
||||
"scripts": {
|
||||
"watch": "vite build --watch --mode development",
|
||||
"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": {
|
||||
"chokidar": "^3.3.1",
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"dependencies": {
|
||||
"@discordapp/twemoji": "14.0.2",
|
||||
"@fortawesome/fontawesome-free": "6.1.2",
|
||||
"@rollup/plugin-alias": "3.1.9",
|
||||
"@rollup/plugin-json": "4.1.0",
|
||||
"@discordapp/twemoji": "15.1.0",
|
||||
"@fortawesome/fontawesome-free": "6.6.0",
|
||||
"@rollup/plugin-alias": "5.1.1",
|
||||
"@rollup/plugin-json": "6.1.0",
|
||||
"@syuilo/aiscript": "0.11.1",
|
||||
"@vitejs/plugin-vue": "3.1.0",
|
||||
"@vue/compiler-sfc": "3.2.39",
|
||||
"@vitejs/plugin-vue": "5.1.4",
|
||||
"@vue/compiler-sfc": "3.5.12",
|
||||
"autobind-decorator": "2.4.0",
|
||||
"autosize": "5.0.1",
|
||||
"blurhash": "1.1.5",
|
||||
"broadcast-channel": "4.17.0",
|
||||
"autosize": "6.0.1",
|
||||
"blurhash": "2.0.5",
|
||||
"broadcast-channel": "7.0.0",
|
||||
"browser-image-resizer": "git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.2",
|
||||
"chart.js": "3.9.1",
|
||||
"chartjs-adapter-date-fns": "2.0.0",
|
||||
"chartjs-plugin-gradient": "0.5.1",
|
||||
"chartjs-plugin-zoom": "1.2.1",
|
||||
"compare-versions": "5.0.1",
|
||||
"chart.js": "4.4.4",
|
||||
"chartjs-adapter-date-fns": "3.0.0",
|
||||
"chartjs-plugin-gradient": "0.6.1",
|
||||
"chartjs-plugin-zoom": "2.0.1",
|
||||
"chokidar": "^4.0.1",
|
||||
"compare-versions": "6.1.1",
|
||||
"cropperjs": "2.0.0-beta",
|
||||
"date-fns": "2.29.3",
|
||||
"date-fns": "4.1.0",
|
||||
"escape-regexp": "0.0.1",
|
||||
"eventemitter3": "4.0.7",
|
||||
"idb-keyval": "6.2.0",
|
||||
"eventemitter3": "5.0.1",
|
||||
"idb-keyval": "6.2.1",
|
||||
"insert-text-at-cursor": "0.3.0",
|
||||
"json5": "2.2.1",
|
||||
"katex": "0.15.6",
|
||||
"matter-js": "0.18.0",
|
||||
"json5": "2.2.3",
|
||||
"katex": "0.16.11",
|
||||
"matter-js": "0.20.0",
|
||||
"mfm-js": "git+https://github.com/sim1222/mfm.js.git",
|
||||
"misetehoshii": "https://github.com/melt-adzuki/misetehoshii",
|
||||
"misskey-js": "0.0.14",
|
||||
"photoswipe": "5.3.2",
|
||||
"photoswipe": "5.4.4",
|
||||
"prismjs": "1.29.0",
|
||||
"punycode": "2.1.1",
|
||||
"punycode": "2.3.1",
|
||||
"querystring": "0.2.1",
|
||||
"rndstr": "1.0.0",
|
||||
"s-age": "1.1.2",
|
||||
"sass": "1.54.9",
|
||||
"sass": "1.79.5",
|
||||
"seedrandom": "3.0.5",
|
||||
"strict-event-emitter-types": "2.0.0",
|
||||
"stringz": "2.1.0",
|
||||
"syuilo-password-strength": "0.0.1",
|
||||
"textarea-caret": "3.1.0",
|
||||
"three": "0.144.0",
|
||||
"throttle-debounce": "5.0.0",
|
||||
"tinycolor2": "1.4.2",
|
||||
"tsc-alias": "1.7.0",
|
||||
"tsconfig-paths": "4.1.0",
|
||||
"three": "0.169.0",
|
||||
"throttle-debounce": "5.0.2",
|
||||
"tinycolor2": "1.6.0",
|
||||
"tsc-alias": "1.8.10",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"twemoji-parser": "14.0.0",
|
||||
"typescript": "4.8.3",
|
||||
"uuid": "9.0.0",
|
||||
"vanilla-tilt": "1.7.2",
|
||||
"vite": "3.1.3",
|
||||
"vue": "3.2.39",
|
||||
"typescript": "5.6.3",
|
||||
"uuid": "10.0.0",
|
||||
"vanilla-tilt": "1.8.1",
|
||||
"vite": "5.4.8",
|
||||
"vue": "3.5.12",
|
||||
"vue-prism-editor": "2.0.0-alpha.2",
|
||||
"vuedraggable": "4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/escape-regexp": "0.0.1",
|
||||
"@types/glob": "8.0.0",
|
||||
"@types/gulp": "4.0.9",
|
||||
"@types/gulp-rename": "2.0.1",
|
||||
"@types/katex": "0.14.0",
|
||||
"@types/matter-js": "0.18.2",
|
||||
"@types/punycode": "2.1.0",
|
||||
"@types/seedrandom": "3.0.2",
|
||||
"@types/throttle-debounce": "5.0.0",
|
||||
"@types/tinycolor2": "1.4.3",
|
||||
"@types/uuid": "8.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "5.38.0",
|
||||
"@typescript-eslint/parser": "5.38.0",
|
||||
"@types/escape-regexp": "0.0.3",
|
||||
"@types/glob": "8.1.0",
|
||||
"@types/gulp": "4.0.17",
|
||||
"@types/gulp-rename": "2.0.6",
|
||||
"@types/katex": "0.16.7",
|
||||
"@types/matter-js": "0.19.7",
|
||||
"@types/punycode": "2.1.4",
|
||||
"@types/seedrandom": "3.0.8",
|
||||
"@types/throttle-debounce": "5.0.2",
|
||||
"@types/tinycolor2": "1.4.6",
|
||||
"@types/uuid": "10.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.8.1",
|
||||
"@typescript-eslint/parser": "8.8.1",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "10.8.0",
|
||||
"eslint": "8.23.1",
|
||||
"eslint-plugin-import": "2.26.0",
|
||||
"eslint-plugin-vue": "9.5.1",
|
||||
"rollup": "2.79.0",
|
||||
"start-server-and-test": "1.14.0"
|
||||
"cypress": "13.15.0",
|
||||
"eslint": "9.12.0",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-vue": "9.29.0",
|
||||
"rollup": "4.24.0",
|
||||
"start-server-and-test": "2.0.8",
|
||||
"vue-tsc": "2.1.6"
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkSwitch from '@/components/form/switch.vue';
|
||||
import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||
@ -51,11 +53,11 @@ const emit = defineEmits<{
|
||||
(ev: 'resolved', reportId: string): void;
|
||||
}>();
|
||||
|
||||
let forward = $ref(props.report.forwarded);
|
||||
let forward = ref(props.report.forwarded);
|
||||
|
||||
function resolve() {
|
||||
os.apiWithDialog('admin/resolve-abuse-user-report', {
|
||||
forward: forward,
|
||||
forward: forward.value,
|
||||
reportId: props.report.id,
|
||||
}).then(() => {
|
||||
emit('resolved', props.report.id);
|
||||
|
@ -130,44 +130,44 @@ const texts = computed(() => {
|
||||
});
|
||||
|
||||
let enabled = true;
|
||||
let majorGraduationColor = $ref<string>();
|
||||
let majorGraduationColor = ref<string>();
|
||||
//let minorGraduationColor = $ref<string>();
|
||||
let sHandColor = $ref<string>();
|
||||
let mHandColor = $ref<string>();
|
||||
let hHandColor = $ref<string>();
|
||||
let nowColor = $ref<string>();
|
||||
let h = $ref<number>(0);
|
||||
let m = $ref<number>(0);
|
||||
let s = $ref<number>(0);
|
||||
let hAngle = $ref<number>(0);
|
||||
let mAngle = $ref<number>(0);
|
||||
let sAngle = $ref<number>(0);
|
||||
let disableSAnimate = $ref(false);
|
||||
let sHandColor = ref<string>();
|
||||
let mHandColor = ref<string>();
|
||||
let hHandColor = ref<string>();
|
||||
let nowColor = ref<string>();
|
||||
let h = ref<number>(0);
|
||||
let m = ref<number>(0);
|
||||
let s = ref<number>(0);
|
||||
let hAngle = ref<number>(0);
|
||||
let mAngle = ref<number>(0);
|
||||
let sAngle = ref<number>(0);
|
||||
let disableSAnimate = ref(false);
|
||||
let sOneRound = false;
|
||||
|
||||
function tick() {
|
||||
const now = new Date();
|
||||
now.setMinutes(now.getMinutes() + (new Date().getTimezoneOffset() + props.offset));
|
||||
s = now.getSeconds();
|
||||
m = now.getMinutes();
|
||||
h = now.getHours();
|
||||
hAngle = Math.PI * (h % (props.twentyfour ? 24 : 12) + (m + s / 60) / 60) / (props.twentyfour ? 12 : 6);
|
||||
mAngle = Math.PI * (m + s / 60) / 30;
|
||||
s.value = now.getSeconds();
|
||||
m.value = now.getMinutes();
|
||||
h.value = now.getHours();
|
||||
hAngle.value = Math.PI * (h.value % (props.twentyfour ? 24 : 12) + (m.value + s.value / 60) / 60) / (props.twentyfour ? 12 : 6);
|
||||
mAngle.value = Math.PI * (m.value + s.value / 60) / 30;
|
||||
if (sOneRound) { // 秒針が一周した際のアニメーションをよしなに処理する(これが無いと秒が59->0になったときに期待したアニメーションにならない)
|
||||
sAngle = Math.PI * 60 / 30;
|
||||
sAngle.value = Math.PI * 60 / 30;
|
||||
window.setTimeout(() => {
|
||||
disableSAnimate = true;
|
||||
disableSAnimate.value = true;
|
||||
window.setTimeout(() => {
|
||||
sAngle = 0;
|
||||
sAngle.value = 0;
|
||||
window.setTimeout(() => {
|
||||
disableSAnimate = false;
|
||||
disableSAnimate.value = false;
|
||||
}, 100);
|
||||
}, 100);
|
||||
}, 700);
|
||||
} else {
|
||||
sAngle = Math.PI * s / 30;
|
||||
sAngle.value = Math.PI * s.value / 30;
|
||||
}
|
||||
sOneRound = s === 59;
|
||||
sOneRound = s.value === 59;
|
||||
}
|
||||
|
||||
tick();
|
||||
@ -176,12 +176,12 @@ function calcColors() {
|
||||
const computedStyle = getComputedStyle(document.documentElement);
|
||||
const dark = tinycolor(computedStyle.getPropertyValue('--bg')).isDark();
|
||||
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)';
|
||||
sHandColor = dark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)';
|
||||
mHandColor = tinycolor(computedStyle.getPropertyValue('--fg')).toHexString();
|
||||
hHandColor = accent;
|
||||
nowColor = accent;
|
||||
sHandColor.value = dark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)';
|
||||
mHandColor.value = tinycolor(computedStyle.getPropertyValue('--fg')).toHexString();
|
||||
hHandColor.value = accent;
|
||||
nowColor.value = accent;
|
||||
}
|
||||
|
||||
calcColors();
|
||||
|
@ -25,7 +25,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onMounted } from 'vue';
|
||||
import { nextTick, onMounted, ref } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
type?: 'button' | 'submit' | 'reset';
|
||||
@ -45,13 +45,13 @@ const emit = defineEmits<{
|
||||
(ev: 'click', payload: MouseEvent): void;
|
||||
}>();
|
||||
|
||||
let el = $ref<HTMLElement | null>(null);
|
||||
let ripples = $ref<HTMLElement | null>(null);
|
||||
let el = ref<HTMLElement | null>(null);
|
||||
let ripples = ref<HTMLElement | null>(null);
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autofocus) {
|
||||
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.left = (evt.clientX - rect.left - 1).toString() + 'px';
|
||||
|
||||
ripples!.appendChild(ripple);
|
||||
ripples.value!.appendChild(ripple);
|
||||
|
||||
const circleCenterX = evt.clientX - rect.left;
|
||||
const circleCenterY = evt.clientY - rect.top;
|
||||
@ -92,7 +92,7 @@ function onMousedown(evt: MouseEvent): void {
|
||||
ripple.style.opacity = '0';
|
||||
}, 1000);
|
||||
window.setTimeout(() => {
|
||||
if (ripples) ripples.removeChild(ripple);
|
||||
if (ripples.value) ripples.value.removeChild(ripple);
|
||||
}, 2000);
|
||||
}
|
||||
</script>
|
||||
|
@ -7,7 +7,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onBeforeUnmount } from 'vue';
|
||||
import { onMounted, onBeforeUnmount, ref } from 'vue';
|
||||
import MkMenu from './MkMenu.vue';
|
||||
import { MenuItem } from './types/menu.vue';
|
||||
import contains from '@/scripts/contains';
|
||||
@ -22,16 +22,16 @@ const emit = defineEmits<{
|
||||
(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(() => {
|
||||
let left = props.ev.pageX + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
||||
let top = props.ev.pageY + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
||||
|
||||
const width = rootEl.offsetWidth;
|
||||
const height = rootEl.offsetHeight;
|
||||
const width = rootEl.value.offsetWidth;
|
||||
const height = rootEl.value.offsetHeight;
|
||||
|
||||
if (left + width - window.pageXOffset > window.innerWidth) {
|
||||
left = window.innerWidth - width + window.pageXOffset;
|
||||
@ -49,8 +49,8 @@ onMounted(() => {
|
||||
left = 0;
|
||||
}
|
||||
|
||||
rootEl.style.top = `${top}px`;
|
||||
rootEl.style.left = `${left}px`;
|
||||
rootEl.value.style.top = `${top}px`;
|
||||
rootEl.value.style.left = `${left}px`;
|
||||
|
||||
for (const el of Array.from(document.querySelectorAll('body *'))) {
|
||||
el.addEventListener('mousedown', onMousedown);
|
||||
@ -64,7 +64,7 @@ onBeforeUnmount(() => {
|
||||
});
|
||||
|
||||
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>
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onMounted } from 'vue';
|
||||
import { nextTick, onMounted, ref } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import Cropper from 'cropperjs';
|
||||
import tinycolor from 'tinycolor2';
|
||||
@ -52,10 +52,10 @@ const props = defineProps<{
|
||||
const imgUrl = `${url}/proxy/image.webp?${query({
|
||||
url: props.file.url,
|
||||
})}`;
|
||||
let dialogEl = $ref<InstanceType<typeof XModalWindow>>();
|
||||
let imgEl = $ref<HTMLImageElement>();
|
||||
let dialogEl = ref<InstanceType<typeof XModalWindow>>();
|
||||
let imgEl = ref<HTMLImageElement>();
|
||||
let cropper: Cropper | null = null;
|
||||
let loading = $ref(true);
|
||||
let loading = ref(true);
|
||||
|
||||
const ok = async () => {
|
||||
const promise = new Promise<misskey.entities.DriveFile>(async (res) => {
|
||||
@ -84,16 +84,16 @@ const ok = async () => {
|
||||
const f = await promise;
|
||||
|
||||
emit('ok', f);
|
||||
dialogEl.close();
|
||||
dialogEl.value.close();
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
emit('cancel');
|
||||
dialogEl.close();
|
||||
dialogEl.value.close();
|
||||
};
|
||||
|
||||
const onImageLoad = () => {
|
||||
loading = false;
|
||||
loading.value = false;
|
||||
|
||||
if (cropper) {
|
||||
cropper.getCropperImage()!.$center('contain');
|
||||
@ -102,7 +102,7 @@ const onImageLoad = () => {
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
cropper = new Cropper(imgEl, {
|
||||
cropper = new Cropper(imgEl.value, {
|
||||
});
|
||||
|
||||
const computedStyle = getComputedStyle(document.documentElement);
|
||||
|
@ -28,7 +28,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, onMounted } from 'vue';
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import * as os from '@/os';
|
||||
import { stream } from '@/stream';
|
||||
@ -43,9 +43,9 @@ const props = withDefaults(defineProps<{
|
||||
large: false,
|
||||
});
|
||||
|
||||
let isFollowing = $ref(props.user.isFollowing);
|
||||
let hasPendingFollowRequestFromYou = $ref(props.user.hasPendingFollowRequestFromYou);
|
||||
let wait = $ref(false);
|
||||
let isFollowing = ref(props.user.isFollowing);
|
||||
let hasPendingFollowRequestFromYou = ref(props.user.hasPendingFollowRequestFromYou);
|
||||
let wait = ref(false);
|
||||
const connection = stream.useChannel('main');
|
||||
|
||||
if (props.user.isFollowing == null) {
|
||||
@ -57,16 +57,16 @@ if (props.user.isFollowing == null) {
|
||||
|
||||
function onFollowChange(user: Misskey.entities.UserDetailed) {
|
||||
if (user.id === props.user.id) {
|
||||
isFollowing = user.isFollowing;
|
||||
hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
||||
isFollowing.value = user.isFollowing;
|
||||
hasPendingFollowRequestFromYou.value = user.hasPendingFollowRequestFromYou;
|
||||
}
|
||||
}
|
||||
|
||||
async function onClick() {
|
||||
wait = true;
|
||||
wait.value = true;
|
||||
|
||||
try {
|
||||
if (isFollowing) {
|
||||
if (isFollowing.value) {
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'warning',
|
||||
text: i18n.t('unfollowConfirm', { name: props.user.name || props.user.username }),
|
||||
@ -78,22 +78,22 @@ async function onClick() {
|
||||
userId: props.user.id
|
||||
});
|
||||
} else {
|
||||
if (hasPendingFollowRequestFromYou) {
|
||||
if (hasPendingFollowRequestFromYou.value) {
|
||||
await os.api('following/requests/cancel', {
|
||||
userId: props.user.id
|
||||
});
|
||||
hasPendingFollowRequestFromYou = false;
|
||||
hasPendingFollowRequestFromYou.value = false;
|
||||
} else {
|
||||
await os.api('following/create', {
|
||||
userId: props.user.id
|
||||
});
|
||||
hasPendingFollowRequestFromYou = true;
|
||||
hasPendingFollowRequestFromYou.value = true;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
wait = false;
|
||||
wait.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import XModalWindow from '@/components/MkModalWindow.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/form/input.vue';
|
||||
@ -45,20 +45,20 @@ const emit = defineEmits<{
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
let dialog: InstanceType<typeof XModalWindow> = $ref();
|
||||
let dialog: InstanceType<typeof XModalWindow> = ref();
|
||||
|
||||
let username = $ref('');
|
||||
let email = $ref('');
|
||||
let processing = $ref(false);
|
||||
let username = ref('');
|
||||
let email = ref('');
|
||||
let processing = ref(false);
|
||||
|
||||
async function onSubmit() {
|
||||
processing = true;
|
||||
processing.value = true;
|
||||
await os.apiWithDialog('request-reset-password', {
|
||||
username,
|
||||
email,
|
||||
username: username.value,
|
||||
email: email.value,
|
||||
});
|
||||
emit('done');
|
||||
dialog.close();
|
||||
dialog.value.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import bytes from '@/filters/bytes';
|
||||
import number from '@/filters/number';
|
||||
@ -28,7 +28,7 @@ const emit = defineEmits<{
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
const modal = $ref<InstanceType<typeof MkModal>>();
|
||||
const modal = ref<InstanceType<typeof MkModal>>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -6,7 +6,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { decode } from 'blurhash';
|
||||
import { defaultStore } from '@/store';
|
||||
|
||||
@ -30,20 +30,20 @@ const imgSrc = defaultStore.state.mediaProxy ?
|
||||
:
|
||||
props.src;
|
||||
|
||||
const canvas = $ref<HTMLCanvasElement>();
|
||||
let loaded = $ref(false);
|
||||
const canvas = ref<HTMLCanvasElement>();
|
||||
let loaded = ref(false);
|
||||
|
||||
function draw() {
|
||||
if (props.hash == null) return;
|
||||
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);
|
||||
imageData.data.set(pixels);
|
||||
ctx!.putImageData(imageData, 0, 0);
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
loaded = true;
|
||||
loaded.value = true;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -10,6 +10,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
import * as misskey from 'misskey-js';
|
||||
import MkMiniChart from '@/components/MkMiniChart.vue';
|
||||
import * as os from '@/os';
|
||||
@ -18,12 +20,12 @@ const props = defineProps<{
|
||||
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 => {
|
||||
// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
|
||||
res.requests.received.splice(0, 1);
|
||||
chartValues = res.requests.received;
|
||||
chartValues.value = res.requests.received;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -48,7 +48,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import {
|
||||
Chart,
|
||||
ArcElement,
|
||||
@ -98,10 +98,10 @@ const props = withDefaults(defineProps<{
|
||||
chartLimit: 90,
|
||||
});
|
||||
|
||||
const chartSpan = $ref<'hour' | 'day'>('hour');
|
||||
const chartSrc = $ref('active-users');
|
||||
let subDoughnutEl = $ref<HTMLCanvasElement>();
|
||||
let pubDoughnutEl = $ref<HTMLCanvasElement>();
|
||||
const chartSpan = ref<'hour' | 'day'>('hour');
|
||||
const chartSrc = ref('active-users');
|
||||
let subDoughnutEl = ref<HTMLCanvasElement>();
|
||||
let pubDoughnutEl = ref<HTMLCanvasElement>();
|
||||
|
||||
const { handler: externalTooltipHandler1 } = useChartTooltip();
|
||||
const { handler: externalTooltipHandler2 } = useChartTooltip();
|
||||
@ -156,7 +156,7 @@ function createDoughnut(chartEl, tooltip, data) {
|
||||
|
||||
onMounted(() => {
|
||||
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,
|
||||
color: x.themeColor,
|
||||
value: x.followersCount,
|
||||
@ -165,7 +165,7 @@ onMounted(() => {
|
||||
},
|
||||
})).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,
|
||||
color: x.themeColor,
|
||||
value: x.followingCount,
|
||||
|
@ -20,7 +20,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import MkModal from '@/components/MkModal.vue';
|
||||
import { navbarItemDef } from '@/navbar';
|
||||
import { instanceName } from '@/config';
|
||||
@ -44,7 +44,7 @@ const preferedModalType = (deviceKind === 'desktop' && props.src != null) ? 'pop
|
||||
deviceKind === 'smartphone' ? 'drawer' :
|
||||
'dialog';
|
||||
|
||||
const modal = $ref<InstanceType<typeof MkModal>>();
|
||||
const modal = ref<InstanceType<typeof MkModal>>();
|
||||
|
||||
const menu = defaultStore.state.menu;
|
||||
|
||||
@ -58,7 +58,7 @@ const items = Object.keys(navbarItemDef).filter(k => !menu.includes(k)).map(k =>
|
||||
}));
|
||||
|
||||
function close() {
|
||||
modal.close();
|
||||
modal.value.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
import { defineAsyncComponent, ref } from 'vue';
|
||||
import { url as local } from '@/config';
|
||||
import { useTooltip } from '@/scripts/use-tooltip';
|
||||
import * as os from '@/os';
|
||||
@ -23,13 +23,13 @@ const self = props.url.startsWith(local);
|
||||
const attr = self ? 'to' : 'href';
|
||||
const target = self ? null : '_blank';
|
||||
|
||||
const el = $ref();
|
||||
const el = ref();
|
||||
|
||||
useTooltip($$(el), (showing) => {
|
||||
useTooltip((el), (showing) => {
|
||||
os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
|
||||
showing,
|
||||
url: props.url,
|
||||
source: el,
|
||||
source: el.value,
|
||||
}, {}, 'closed');
|
||||
});
|
||||
</script>
|
||||
|
@ -26,7 +26,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import { ColdDeviceStorage } from '@/store';
|
||||
|
||||
@ -35,16 +35,16 @@ const props = withDefaults(defineProps<{
|
||||
}>(), {
|
||||
});
|
||||
|
||||
const audioEl = $ref<HTMLAudioElement | null>();
|
||||
let hide = $ref(true);
|
||||
const audioEl = ref<HTMLAudioElement | null>();
|
||||
let hide = ref(true);
|
||||
|
||||
function volumechange() {
|
||||
if (audioEl) ColdDeviceStorage.set('mediaVolume', audioEl.volume);
|
||||
if (audioEl.value) ColdDeviceStorage.set('mediaVolume', audioEl.value.volume);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// if (audioEl) audioEl.volume = ColdDeviceStorage.get('mediaVolume');
|
||||
if (audioEl) audioEl.volume = 0.2;
|
||||
if (audioEl.value) audioEl.value.volume = 0.2;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch } from 'vue';
|
||||
import { watch, ref } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
|
||||
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
|
||||
@ -32,7 +32,7 @@ const props = defineProps<{
|
||||
raw?: boolean;
|
||||
}>();
|
||||
|
||||
let hide = $ref(true);
|
||||
let hide = ref(true);
|
||||
|
||||
const url = (props.raw || defaultStore.state.loadRawImages)
|
||||
? props.image.url
|
||||
@ -42,7 +42,7 @@ const url = (props.raw || defaultStore.state.loadRawImages)
|
||||
|
||||
// Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする
|
||||
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,
|
||||
immediate: true,
|
||||
|
@ -32,11 +32,11 @@ const props = defineProps<{
|
||||
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'));
|
||||
|
||||
onMounted(() => {
|
||||
if (videoEl) videoEl.volume = 0.2;
|
||||
if (videoEl.value) videoEl.value.volume = 0.2;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -56,7 +56,7 @@
|
||||
</template>
|
||||
|
||||
<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 FormSwitch from '@/components/form/switch.vue';
|
||||
import { MenuItem, InnerMenuItem, MenuPending, MenuAction } from '@/types/menu';
|
||||
@ -78,19 +78,19 @@ const emit = defineEmits<{
|
||||
(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,
|
||||
'down|j|tab': focusDown,
|
||||
'esc': close,
|
||||
}));
|
||||
|
||||
let childShowingItem = $ref<MenuItem | null>();
|
||||
let childShowingItem = ref<MenuItem | null>();
|
||||
|
||||
watch(() => props.items, () => {
|
||||
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
|
||||
items[i] = { type: 'pending' };
|
||||
item.then(actualItem => {
|
||||
items2[i] = actualItem;
|
||||
items2.value[i] = actualItem;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
items2 = items as InnerMenuItem[];
|
||||
items2.value = items as InnerMenuItem[];
|
||||
}, {
|
||||
immediate: true,
|
||||
});
|
||||
|
||||
let childMenu = $ref<MenuItem[] | null>();
|
||||
let childTarget = $ref<HTMLElement | null>();
|
||||
let childMenu = ref<MenuItem[] | null>();
|
||||
let childTarget = ref<HTMLElement | null>();
|
||||
|
||||
function closeChild() {
|
||||
childMenu = null;
|
||||
childShowingItem = null;
|
||||
childMenu.value = null;
|
||||
childShowingItem.value = null;
|
||||
}
|
||||
|
||||
function childActioned() {
|
||||
@ -125,8 +125,8 @@ function childActioned() {
|
||||
}
|
||||
|
||||
function onGlobalMousedown(event: MouseEvent) {
|
||||
if (childTarget && (event.target === childTarget || childTarget.contains(event.target))) return;
|
||||
if (child && child.checkHit(event)) return;
|
||||
if (childTarget.value && (event.target === childTarget.value || childTarget.value.contains(event.target))) return;
|
||||
if (child.value && child.value.checkHit(event)) return;
|
||||
closeChild();
|
||||
}
|
||||
|
||||
@ -145,9 +145,9 @@ async function showChildren(item: MenuItem, ev: MouseEvent) {
|
||||
os.popupMenu(item.children, ev.currentTarget ?? ev.target);
|
||||
close();
|
||||
} else {
|
||||
childTarget = ev.currentTarget ?? ev.target;
|
||||
childMenu = item.children;
|
||||
childShowingItem = item;
|
||||
childTarget.value = ev.currentTarget ?? ev.target;
|
||||
childMenu.value = item.children;
|
||||
childShowingItem.value = item;
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,7 +171,7 @@ function focusDown() {
|
||||
onMounted(() => {
|
||||
if (props.viaKeyboard) {
|
||||
nextTick(() => {
|
||||
focusNext(itemsEl.children[0], true, false);
|
||||
focusNext(itemsEl.value.children[0], true, false);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onUnmounted, watch } from 'vue';
|
||||
import { onUnmounted, watch, ref } from 'vue';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import { useInterval } from '@/scripts/use-interval';
|
||||
@ -38,11 +38,11 @@ const props = defineProps<{
|
||||
const viewBoxX = 50;
|
||||
const viewBoxY = 50;
|
||||
const gradientId = uuid();
|
||||
let polylinePoints = $ref('');
|
||||
let polygonPoints = $ref('');
|
||||
let headX = $ref<number | null>(null);
|
||||
let headY = $ref<number | null>(null);
|
||||
let clock = $ref<number | null>(null);
|
||||
let polylinePoints = ref('');
|
||||
let polygonPoints = ref('');
|
||||
let headX = ref<number | null>(null);
|
||||
let headY = ref<number | null>(null);
|
||||
let clock = ref<number | null>(null);
|
||||
const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent'));
|
||||
const color = accent.toRgbString();
|
||||
|
||||
@ -55,12 +55,12 @@ function draw(): void {
|
||||
(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];
|
||||
headY = _polylinePoints[_polylinePoints.length - 1][1];
|
||||
headX.value = _polylinePoints[_polylinePoints.length - 1][0];
|
||||
headY.value = _polylinePoints[_polylinePoints.length - 1][1];
|
||||
}
|
||||
|
||||
watch(() => props.src, draw, { immediate: true });
|
||||
|
@ -10,7 +10,7 @@
|
||||
</template>
|
||||
|
||||
<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 { isTouchUsing } from '@/scripts/touch';
|
||||
import { defaultStore } from '@/store';
|
||||
@ -57,13 +57,13 @@ const emit = defineEmits<{
|
||||
|
||||
provide('modal', true);
|
||||
|
||||
let maxHeight = $ref<number>();
|
||||
let fixed = $ref(false);
|
||||
let transformOrigin = $ref('center');
|
||||
let showing = $ref(true);
|
||||
let content = $ref<HTMLElement>();
|
||||
let maxHeight = ref<number>();
|
||||
let fixed = ref(false);
|
||||
let transformOrigin = ref('center');
|
||||
let showing = ref(true);
|
||||
let content = ref<HTMLElement>();
|
||||
const zIndex = os.claimZIndex(props.zPriority);
|
||||
const type = $computed(() => {
|
||||
const type = computed(() => {
|
||||
if (props.preferType === 'auto') {
|
||||
if (!defaultStore.state.disableDrawer && isTouchUsing && deviceKind === 'smartphone') {
|
||||
return 'drawer';
|
||||
@ -80,7 +80,7 @@ let contentClicking = false;
|
||||
const close = () => {
|
||||
// eslint-disable-next-line vue/no-mutating-props
|
||||
if (props.src) props.src.style.pointerEvents = 'auto';
|
||||
showing = false;
|
||||
showing.value = false;
|
||||
emit('close');
|
||||
};
|
||||
|
||||
@ -89,8 +89,8 @@ const onBgClick = () => {
|
||||
emit('click');
|
||||
};
|
||||
|
||||
if (type === 'drawer') {
|
||||
maxHeight = window.innerHeight / 1.5;
|
||||
if (type.value === 'drawer') {
|
||||
maxHeight.value = window.innerHeight / 1.5;
|
||||
}
|
||||
|
||||
const keymap = {
|
||||
@ -101,21 +101,21 @@ const MARGIN = 16;
|
||||
|
||||
const align = () => {
|
||||
if (props.src == null) return;
|
||||
if (type === 'drawer') return;
|
||||
if (type === 'dialog') return;
|
||||
if (type.value === 'drawer') return;
|
||||
if (type.value === 'dialog') return;
|
||||
|
||||
if (content == null) return;
|
||||
if (content.value == null) return;
|
||||
|
||||
const srcRect = props.src.getBoundingClientRect();
|
||||
|
||||
const width = content!.offsetWidth;
|
||||
const height = content!.offsetHeight;
|
||||
const width = content.value!.offsetWidth;
|
||||
const height = content.value!.offsetHeight;
|
||||
|
||||
let left;
|
||||
let top;
|
||||
|
||||
const x = srcRect.left + (fixed ? 0 : window.pageXOffset);
|
||||
const y = srcRect.top + (fixed ? 0 : window.pageYOffset);
|
||||
const x = srcRect.left + (fixed.value ? 0 : window.pageXOffset);
|
||||
const y = srcRect.top + (fixed.value ? 0 : window.pageYOffset);
|
||||
|
||||
if (props.anchor.x === 'center') {
|
||||
left = x + (props.src.offsetWidth / 2) - (width / 2);
|
||||
@ -133,7 +133,7 @@ const align = () => {
|
||||
top = y + props.src.offsetHeight;
|
||||
}
|
||||
|
||||
if (fixed) {
|
||||
if (fixed.value) {
|
||||
// 画面から横にはみ出る場合
|
||||
if (left + width > window.innerWidth) {
|
||||
left = window.innerWidth - width;
|
||||
@ -146,16 +146,16 @@ const align = () => {
|
||||
if (top + height > (window.innerHeight - MARGIN)) {
|
||||
if (props.noOverlap && props.anchor.x === 'center') {
|
||||
if (underSpace >= (upperSpace / 3)) {
|
||||
maxHeight = underSpace;
|
||||
maxHeight.value = underSpace;
|
||||
} else {
|
||||
maxHeight = upperSpace;
|
||||
maxHeight.value = upperSpace;
|
||||
top = (upperSpace + MARGIN) - height;
|
||||
}
|
||||
} else {
|
||||
top = (window.innerHeight - MARGIN) - height;
|
||||
}
|
||||
} else {
|
||||
maxHeight = underSpace;
|
||||
maxHeight.value = underSpace;
|
||||
}
|
||||
} else {
|
||||
// 画面から横にはみ出る場合
|
||||
@ -170,16 +170,16 @@ const align = () => {
|
||||
if (top + height - window.pageYOffset > (window.innerHeight - MARGIN)) {
|
||||
if (props.noOverlap && props.anchor.x === 'center') {
|
||||
if (underSpace >= (upperSpace / 3)) {
|
||||
maxHeight = underSpace;
|
||||
maxHeight.value = underSpace;
|
||||
} else {
|
||||
maxHeight = upperSpace;
|
||||
maxHeight.value = upperSpace;
|
||||
top = window.pageYOffset + ((upperSpace + MARGIN) - height);
|
||||
}
|
||||
} else {
|
||||
top = (window.innerHeight - MARGIN) - height + window.pageYOffset - 1;
|
||||
}
|
||||
} else {
|
||||
maxHeight = underSpace;
|
||||
maxHeight.value = underSpace;
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,29 +194,29 @@ const align = () => {
|
||||
let transformOriginX = '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';
|
||||
} else if ((top + height) <= srcRect.top + (fixed ? 0 : window.pageYOffset)) {
|
||||
} else if ((top + height) <= srcRect.top + (fixed.value ? 0 : window.pageYOffset)) {
|
||||
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';
|
||||
} else if ((left + width) <= srcRect.left + (fixed ? 0 : window.pageXOffset)) {
|
||||
} else if ((left + width) <= srcRect.left + (fixed.value ? 0 : window.pageXOffset)) {
|
||||
transformOriginX = 'right';
|
||||
}
|
||||
|
||||
transformOrigin = `${transformOriginX} ${transformOriginY}`;
|
||||
transformOrigin.value = `${transformOriginX} ${transformOriginY}`;
|
||||
|
||||
content.style.left = left + 'px';
|
||||
content.style.top = top + 'px';
|
||||
content.value.style.left = left + 'px';
|
||||
content.value.style.top = top + 'px';
|
||||
};
|
||||
|
||||
const onOpened = () => {
|
||||
emit('opened');
|
||||
|
||||
// モーダルコンテンツにマウスボタンが押され、コンテンツ外でマウスボタンが離されたときにモーダルバックグラウンドクリックと判定させないためにマウスイベントを監視しフラグ管理する
|
||||
const el = content!.children[0];
|
||||
const el = content.value!.children[0];
|
||||
el.addEventListener('mousedown', ev => {
|
||||
contentClicking = true;
|
||||
window.addEventListener('mouseup', ev => {
|
||||
@ -234,7 +234,7 @@ onMounted(() => {
|
||||
// eslint-disable-next-line vue/no-mutating-props
|
||||
props.src.style.pointerEvents = 'none';
|
||||
}
|
||||
fixed = (type === 'drawer') || (getFixedContainer(props.src) != null);
|
||||
fixed.value = (type.value === 'drawer') || (getFixedContainer(props.src) != null);
|
||||
|
||||
await nextTick();
|
||||
|
||||
@ -244,7 +244,7 @@ onMounted(() => {
|
||||
nextTick(() => {
|
||||
new ResizeObserver((entries, observer) => {
|
||||
align();
|
||||
}).observe(content!);
|
||||
}).observe(content.value!);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ComputedRef, provide } from 'vue';
|
||||
import { ComputedRef, provide, ref, computed } from 'vue';
|
||||
import MkModal from '@/components/MkModal.vue';
|
||||
import { popout as _popout } from '@/scripts/popout';
|
||||
import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||
@ -47,26 +47,26 @@ router.addListener('push', ctx => {
|
||||
|
||||
});
|
||||
|
||||
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
|
||||
let rootEl = $ref();
|
||||
let modal = $ref<InstanceType<typeof MkModal>>();
|
||||
let path = $ref(props.initialPath);
|
||||
let width = $ref(860);
|
||||
let height = $ref(660);
|
||||
let pageMetadata = ref<null | ComputedRef<PageMetadata>>();
|
||||
let rootEl = ref();
|
||||
let modal = ref<InstanceType<typeof MkModal>>();
|
||||
let path = ref(props.initialPath);
|
||||
let width = ref(860);
|
||||
let height = ref(660);
|
||||
const history = [];
|
||||
|
||||
provide('router', router);
|
||||
provideMetadataReceiver((info) => {
|
||||
pageMetadata = info;
|
||||
pageMetadata.value = info;
|
||||
});
|
||||
provide('shouldOmitHeaderTitle', true);
|
||||
provide('shouldHeaderThin', true);
|
||||
|
||||
const pageUrl = $computed(() => url + path);
|
||||
const contextmenu = $computed(() => {
|
||||
const pageUrl = computed(() => url + path.value);
|
||||
const contextmenu = computed(() => {
|
||||
return [{
|
||||
type: 'label',
|
||||
text: path,
|
||||
text: path.value,
|
||||
}, {
|
||||
icon: 'fas fa-expand-alt',
|
||||
text: i18n.ts.showInPage,
|
||||
@ -79,14 +79,14 @@ const contextmenu = $computed(() => {
|
||||
icon: 'fas fa-external-link-alt',
|
||||
text: i18n.ts.openInNewTab,
|
||||
action: () => {
|
||||
window.open(pageUrl, '_blank');
|
||||
modal.close();
|
||||
window.open(pageUrl.value, '_blank');
|
||||
modal.value.close();
|
||||
},
|
||||
}, {
|
||||
icon: 'fas fa-link',
|
||||
text: i18n.ts.copyLink,
|
||||
action: () => {
|
||||
copyToClipboard(pageUrl);
|
||||
copyToClipboard(pageUrl.value);
|
||||
},
|
||||
}];
|
||||
});
|
||||
@ -101,17 +101,17 @@ function back() {
|
||||
}
|
||||
|
||||
function expand() {
|
||||
mainRouter.push(path);
|
||||
modal.close();
|
||||
mainRouter.push(path.value);
|
||||
modal.value.close();
|
||||
}
|
||||
|
||||
function popout() {
|
||||
_popout(path, rootEl);
|
||||
modal.close();
|
||||
_popout(path.value, rootEl.value);
|
||||
modal.value.close();
|
||||
}
|
||||
|
||||
function onContextmenu(ev: MouseEvent) {
|
||||
os.contextMenu(contextmenu, ev);
|
||||
os.contextMenu(contextmenu.value, ev);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted } from 'vue';
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
import MkModal from './MkModal.vue';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
@ -41,14 +41,14 @@ const emit = defineEmits<{
|
||||
(event: 'ok'): void;
|
||||
}>();
|
||||
|
||||
let modal = $ref<InstanceType<typeof MkModal>>();
|
||||
let rootEl = $ref<HTMLElement>();
|
||||
let headerEl = $ref<HTMLElement>();
|
||||
let bodyWidth = $ref(0);
|
||||
let bodyHeight = $ref(0);
|
||||
let modal = ref<InstanceType<typeof MkModal>>();
|
||||
let rootEl = ref<HTMLElement>();
|
||||
let headerEl = ref<HTMLElement>();
|
||||
let bodyWidth = ref(0);
|
||||
let bodyHeight = ref(0);
|
||||
|
||||
const close = () => {
|
||||
modal.close();
|
||||
modal.value.close();
|
||||
};
|
||||
|
||||
const onBgClick = () => {
|
||||
@ -64,14 +64,14 @@ const onKeydown = (evt) => {
|
||||
};
|
||||
|
||||
const ro = new ResizeObserver((entries, observer) => {
|
||||
bodyWidth = rootEl.offsetWidth;
|
||||
bodyHeight = rootEl.offsetHeight - headerEl.offsetHeight;
|
||||
bodyWidth.value = rootEl.value.offsetWidth;
|
||||
bodyHeight.value = rootEl.value.offsetHeight - headerEl.value.offsetHeight;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
bodyWidth = rootEl.offsetWidth;
|
||||
bodyHeight = rootEl.offsetHeight - headerEl.offsetHeight;
|
||||
ro.observe(rootEl);
|
||||
bodyWidth.value = rootEl.value.offsetWidth;
|
||||
bodyHeight.value = rootEl.value.offsetHeight - headerEl.value.offsetHeight;
|
||||
ro.observe(rootEl.value);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
|
@ -137,24 +137,24 @@ const props = defineProps<{
|
||||
|
||||
const inChannel = inject('inChannel', null);
|
||||
|
||||
let note = $ref(JSON.parse(JSON.stringify(props.note)));
|
||||
let note = ref(JSON.parse(JSON.stringify(props.note)));
|
||||
|
||||
// plugin
|
||||
if (noteViewInterruptors.length > 0) {
|
||||
onMounted(async () => {
|
||||
let result = JSON.parse(JSON.stringify(note));
|
||||
let result = JSON.parse(JSON.stringify(note.value));
|
||||
for (const interruptor of noteViewInterruptors) {
|
||||
result = await interruptor.handler(result);
|
||||
}
|
||||
note = result;
|
||||
note.value = result;
|
||||
});
|
||||
}
|
||||
|
||||
const isRenote = (
|
||||
note.renote != null &&
|
||||
note.text == null &&
|
||||
note.fileIds.length === 0 &&
|
||||
note.poll == null
|
||||
note.value.renote != null &&
|
||||
note.value.text == null &&
|
||||
note.value.fileIds.length === 0 &&
|
||||
note.value.poll == null
|
||||
);
|
||||
|
||||
const el = ref<HTMLElement>();
|
||||
@ -162,20 +162,20 @@ const menuButton = ref<HTMLElement>();
|
||||
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
||||
const renoteTime = ref<HTMLElement>();
|
||||
const reactButton = ref<HTMLElement>();
|
||||
let appearNote = $computed(() => isRenote ? note.renote as misskey.entities.Note : note);
|
||||
const isMyRenote = $i && ($i.id === note.userId);
|
||||
let appearNote = computed(() => isRenote ? note.value.renote as misskey.entities.Note : note.value);
|
||||
const isMyRenote = $i && ($i.id === note.value.userId);
|
||||
const showContent = ref(false);
|
||||
const isLong = (appearNote.cw == null && appearNote.text != null && (
|
||||
(appearNote.text.split('\n').length > 9) ||
|
||||
(appearNote.text.length > 500)
|
||||
const isLong = (appearNote.value.cw == null && appearNote.value.text != null && (
|
||||
(appearNote.value.text.split('\n').length > 9) ||
|
||||
(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 muted = ref(checkWordMute(appearNote, $i, defaultStore.state.mutedWords));
|
||||
const muted = ref(checkWordMute(appearNote.value, $i, defaultStore.state.mutedWords));
|
||||
const translation = ref(null);
|
||||
const translating = ref(false);
|
||||
const urls = appearNote.text ? extractUrlFromMfm(mfm.parse(appearNote.text)) : null;
|
||||
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance);
|
||||
const urls = appearNote.value.text ? extractUrlFromMfm(mfm.parse(appearNote.value.text)) : null;
|
||||
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance);
|
||||
|
||||
const keymap = {
|
||||
'r': () => reply(true),
|
||||
@ -190,14 +190,14 @@ const keymap = {
|
||||
|
||||
useNoteCapture({
|
||||
rootEl: el,
|
||||
note: $$(appearNote),
|
||||
note: (appearNote),
|
||||
isDeletedRef: isDeleted,
|
||||
});
|
||||
|
||||
function reply(viaKeyboard = false): void {
|
||||
pleaseLogin();
|
||||
os.post({
|
||||
reply: appearNote,
|
||||
reply: appearNote.value,
|
||||
animation: !viaKeyboard,
|
||||
}, () => {
|
||||
focus();
|
||||
@ -209,12 +209,12 @@ function react(viaKeyboard = false): void {
|
||||
blur();
|
||||
reactionPicker.show(reactButton.value, results => {
|
||||
os.api('notes/reactions/create', {
|
||||
noteId: appearNote.id,
|
||||
noteId: appearNote.value.id,
|
||||
reaction: results.reaction,
|
||||
});
|
||||
if (results.withRenote) {
|
||||
os.api('notes/create', {
|
||||
renoteId: appearNote.id,
|
||||
renoteId: appearNote.value.id,
|
||||
isRenote: true,
|
||||
});
|
||||
}
|
||||
@ -247,12 +247,12 @@ function onContextmenu(ev: MouseEvent): void {
|
||||
ev.preventDefault();
|
||||
react();
|
||||
} 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 {
|
||||
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,
|
||||
}).then(focus);
|
||||
}
|
||||
@ -265,7 +265,7 @@ function showRenoteMenu(viaKeyboard = false): void {
|
||||
danger: true,
|
||||
action: () => {
|
||||
os.api('notes/delete', {
|
||||
noteId: note.id,
|
||||
noteId: note.value.id,
|
||||
});
|
||||
isDeleted.value = true;
|
||||
},
|
||||
@ -292,7 +292,7 @@ function focusAfter() {
|
||||
|
||||
function readPromo() {
|
||||
os.api('promo/read', {
|
||||
noteId: appearNote.id,
|
||||
noteId: appearNote.value.id,
|
||||
});
|
||||
isDeleted.value = true;
|
||||
}
|
||||
|
@ -147,24 +147,24 @@ const props = defineProps<{
|
||||
|
||||
const inChannel = inject('inChannel', null);
|
||||
|
||||
let note = $ref(JSON.parse(JSON.stringify(props.note)));
|
||||
let note = ref(JSON.parse(JSON.stringify(props.note)));
|
||||
|
||||
// plugin
|
||||
if (noteViewInterruptors.length > 0) {
|
||||
onMounted(async () => {
|
||||
let result = JSON.parse(JSON.stringify(note));
|
||||
let result = JSON.parse(JSON.stringify(note.value));
|
||||
for (const interruptor of noteViewInterruptors) {
|
||||
result = await interruptor.handler(result);
|
||||
}
|
||||
note = result;
|
||||
note.value = result;
|
||||
});
|
||||
}
|
||||
|
||||
const isRenote = (
|
||||
note.renote != null &&
|
||||
note.text == null &&
|
||||
note.fileIds.length === 0 &&
|
||||
note.poll == null
|
||||
note.value.renote != null &&
|
||||
note.value.text == null &&
|
||||
note.value.fileIds.length === 0 &&
|
||||
note.value.poll == null
|
||||
);
|
||||
|
||||
const el = ref<HTMLElement>();
|
||||
@ -172,15 +172,15 @@ const menuButton = ref<HTMLElement>();
|
||||
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
||||
const renoteTime = ref<HTMLElement>();
|
||||
const reactButton = ref<HTMLElement>();
|
||||
let appearNote = $computed(() => isRenote ? note.renote as misskey.entities.Note : note);
|
||||
const isMyRenote = $i && ($i.id === note.userId);
|
||||
let appearNote = computed(() => isRenote ? note.value.renote as misskey.entities.Note : note.value);
|
||||
const isMyRenote = $i && ($i.id === note.value.userId);
|
||||
const showContent = 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 translating = ref(false);
|
||||
const urls = appearNote.text ? extractUrlFromMfm(mfm.parse(appearNote.text)) : null;
|
||||
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance);
|
||||
const urls = appearNote.value.text ? extractUrlFromMfm(mfm.parse(appearNote.value.text)) : null;
|
||||
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance);
|
||||
const conversation = ref<misskey.entities.Note[]>([]);
|
||||
const replies = ref<misskey.entities.Note[]>([]);
|
||||
|
||||
@ -195,14 +195,14 @@ const keymap = {
|
||||
|
||||
useNoteCapture({
|
||||
rootEl: el,
|
||||
note: $$(appearNote),
|
||||
note: (appearNote),
|
||||
isDeletedRef: isDeleted,
|
||||
});
|
||||
|
||||
function reply(viaKeyboard = false): void {
|
||||
pleaseLogin();
|
||||
os.post({
|
||||
reply: appearNote,
|
||||
reply: appearNote.value,
|
||||
animation: !viaKeyboard,
|
||||
}, () => {
|
||||
focus();
|
||||
@ -214,7 +214,7 @@ function react(viaKeyboard = false): void {
|
||||
blur();
|
||||
reactionPicker.show(reactButton.value, reaction => {
|
||||
os.api('notes/reactions/create', {
|
||||
noteId: appearNote.id,
|
||||
noteId: appearNote.value.id,
|
||||
reaction: reaction.reaction,
|
||||
});
|
||||
}, () => {
|
||||
@ -244,12 +244,12 @@ function onContextmenu(ev: MouseEvent): void {
|
||||
ev.preventDefault();
|
||||
react();
|
||||
} 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 {
|
||||
os.popupMenu(getNoteMenu({ note: note, translating, translation, menuButton, isDeleted }), menuButton.value, {
|
||||
os.popupMenu(getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted }), menuButton.value, {
|
||||
viaKeyboard,
|
||||
}).then(focus);
|
||||
}
|
||||
@ -262,7 +262,7 @@ function showRenoteMenu(viaKeyboard = false): void {
|
||||
danger: true,
|
||||
action: () => {
|
||||
os.api('notes/delete', {
|
||||
noteId: note.id,
|
||||
noteId: note.value.id,
|
||||
});
|
||||
isDeleted.value = true;
|
||||
},
|
||||
@ -280,15 +280,15 @@ function blur() {
|
||||
}
|
||||
|
||||
os.api('notes/children', {
|
||||
noteId: appearNote.id,
|
||||
noteId: appearNote.value.id,
|
||||
limit: 30,
|
||||
}).then(res => {
|
||||
replies.value = res;
|
||||
});
|
||||
|
||||
if (appearNote.replyId) {
|
||||
if (appearNote.value.replyId) {
|
||||
os.api('notes/conversation', {
|
||||
noteId: appearNote.replyId,
|
||||
noteId: appearNote.value.replyId,
|
||||
}).then(res => {
|
||||
conversation.value = res.reverse();
|
||||
});
|
||||
|
@ -17,7 +17,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import XNoteHeader from '@/components/MkNoteHeader.vue';
|
||||
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
|
||||
@ -28,7 +28,7 @@ const props = defineProps<{
|
||||
pinned?: boolean;
|
||||
}>();
|
||||
|
||||
const showContent = $ref(false);
|
||||
const showContent = ref(false);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -25,7 +25,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import XNoteHeader from '@/components/MkNoteHeader.vue';
|
||||
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
|
||||
@ -44,15 +44,15 @@ const props = withDefaults(defineProps<{
|
||||
depth: 1,
|
||||
});
|
||||
|
||||
let showContent = $ref(false);
|
||||
let replies: misskey.entities.Note[] = $ref([]);
|
||||
let showContent = ref(false);
|
||||
let replies: misskey.entities.Note[] = ref([]);
|
||||
|
||||
if (props.detail) {
|
||||
os.api('notes/children', {
|
||||
noteId: props.note.id,
|
||||
limit: 5,
|
||||
}).then(res => {
|
||||
replies = res;
|
||||
replies.value = res;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
@ -28,7 +28,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { notificationTypes } from 'misskey-js';
|
||||
import MkSwitch from './form/switch.vue';
|
||||
import MkInfo from './MkInfo.vue';
|
||||
@ -49,39 +49,39 @@ const props = withDefaults(defineProps<{
|
||||
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 useGlobalSetting = $ref((includingTypes === null || includingTypes.length === 0) && props.showGlobalToggle);
|
||||
let typesMap = ref<Record<typeof notificationTypes[number], boolean>>({});
|
||||
let useGlobalSetting = ref((includingTypes.value === null || includingTypes.value.length === 0) && props.showGlobalToggle);
|
||||
|
||||
for (const ntype of notificationTypes) {
|
||||
typesMap[ntype] = includingTypes.includes(ntype);
|
||||
typesMap.value[ntype] = includingTypes.value.includes(ntype);
|
||||
}
|
||||
|
||||
function ok() {
|
||||
if (useGlobalSetting) {
|
||||
if (useGlobalSetting.value) {
|
||||
emit('done', { includingTypes: null });
|
||||
} else {
|
||||
emit('done', {
|
||||
includingTypes: (Object.keys(typesMap) as typeof notificationTypes[number][])
|
||||
.filter(type => typesMap[type]),
|
||||
includingTypes: (Object.keys(typesMap.value) as typeof notificationTypes[number][])
|
||||
.filter(type => typesMap.value[type]),
|
||||
});
|
||||
}
|
||||
|
||||
dialog.close();
|
||||
dialog.value.close();
|
||||
}
|
||||
|
||||
function disableAll() {
|
||||
for (const type in typesMap) {
|
||||
typesMap[type as typeof notificationTypes[number]] = false;
|
||||
for (const type in typesMap.value) {
|
||||
typesMap.value[type as typeof notificationTypes[number]] = false;
|
||||
}
|
||||
}
|
||||
|
||||
function enableAll() {
|
||||
for (const type in typesMap) {
|
||||
typesMap[type as typeof notificationTypes[number]] = true;
|
||||
for (const type in typesMap.value) {
|
||||
typesMap.value[type as typeof notificationTypes[number]] = true;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -7,7 +7,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import XNotification from '@/components/MkNotification.vue';
|
||||
import * as os from '@/os';
|
||||
|
||||
@ -20,11 +20,11 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const zIndex = os.claimZIndex('high');
|
||||
let showing = $ref(true);
|
||||
let showing = ref(true);
|
||||
|
||||
onMounted(() => {
|
||||
window.setTimeout(() => {
|
||||
showing = false;
|
||||
showing.value = false;
|
||||
}, 6000);
|
||||
});
|
||||
</script>
|
||||
|
@ -24,7 +24,7 @@
|
||||
</template>
|
||||
|
||||
<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 XWindow from '@/components/MkWindow.vue';
|
||||
import { popout as _popout } from '@/scripts/popout';
|
||||
@ -46,16 +46,16 @@ defineEmits<{
|
||||
|
||||
const router = new Router(routes, props.initialPath);
|
||||
|
||||
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
|
||||
let windowEl = $ref<InstanceType<typeof XWindow>>();
|
||||
const history = $ref<{ path: string; key: any; }[]>([{
|
||||
let pageMetadata = ref<null | ComputedRef<PageMetadata>>();
|
||||
let windowEl = ref<InstanceType<typeof XWindow>>();
|
||||
const history = ref<{ path: string; key: any; }[]>([{
|
||||
path: router.getCurrentPath(),
|
||||
key: router.getCurrentKey(),
|
||||
}]);
|
||||
const buttonsLeft = $computed(() => {
|
||||
const buttonsLeft = computed(() => {
|
||||
const buttons = [];
|
||||
|
||||
if (history.length > 1) {
|
||||
if (history.value.length > 1) {
|
||||
buttons.push({
|
||||
icon: 'fas fa-arrow-left',
|
||||
onClick: back,
|
||||
@ -64,7 +64,7 @@ const buttonsLeft = $computed(() => {
|
||||
|
||||
return buttons;
|
||||
});
|
||||
const buttonsRight = $computed(() => {
|
||||
const buttonsRight = computed(() => {
|
||||
const buttons = [{
|
||||
icon: 'fas fa-expand-alt',
|
||||
title: i18n.ts.showInPage,
|
||||
@ -75,17 +75,17 @@ const buttonsRight = $computed(() => {
|
||||
});
|
||||
|
||||
router.addListener('push', ctx => {
|
||||
history.push({ path: ctx.path, key: ctx.key });
|
||||
history.value.push({ path: ctx.path, key: ctx.key });
|
||||
});
|
||||
|
||||
provide('router', router);
|
||||
provideMetadataReceiver((info) => {
|
||||
pageMetadata = info;
|
||||
pageMetadata.value = info;
|
||||
});
|
||||
provide('shouldOmitHeaderTitle', true);
|
||||
provide('shouldHeaderThin', true);
|
||||
|
||||
const contextmenu = $computed(() => ([{
|
||||
const contextmenu = computed(() => ([{
|
||||
icon: 'fas fa-expand-alt',
|
||||
text: i18n.ts.showInPage,
|
||||
action: expand,
|
||||
@ -98,7 +98,7 @@ const contextmenu = $computed(() => ([{
|
||||
text: i18n.ts.openInNewTab,
|
||||
action: () => {
|
||||
window.open(url + router.getCurrentPath(), '_blank');
|
||||
windowEl.close();
|
||||
windowEl.value.close();
|
||||
},
|
||||
}, {
|
||||
icon: 'fas fa-link',
|
||||
@ -109,26 +109,26 @@ const contextmenu = $computed(() => ([{
|
||||
}]));
|
||||
|
||||
function menu(ev) {
|
||||
os.popupMenu(contextmenu, ev.currentTarget ?? ev.target);
|
||||
os.popupMenu(contextmenu.value, ev.currentTarget ?? ev.target);
|
||||
}
|
||||
|
||||
function back() {
|
||||
history.pop();
|
||||
router.replace(history[history.length - 1].path, history[history.length - 1].key);
|
||||
history.value.pop();
|
||||
router.replace(history.value[history.value.length - 1].path, history.value[history.value.length - 1].key);
|
||||
}
|
||||
|
||||
function close() {
|
||||
windowEl.close();
|
||||
windowEl.value.close();
|
||||
}
|
||||
|
||||
function expand() {
|
||||
mainRouter.push(router.getCurrentPath(), 'forcePage');
|
||||
windowEl.close();
|
||||
windowEl.value.close();
|
||||
}
|
||||
|
||||
function popout() {
|
||||
_popout(router.getCurrentPath(), windowEl.$el);
|
||||
windowEl.close();
|
||||
_popout(router.getCurrentPath(), windowEl.value.$el);
|
||||
windowEl.value.close();
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
|
@ -5,7 +5,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import MkModal from './MkModal.vue';
|
||||
import MkMenu from './MkMenu.vue';
|
||||
import { MenuItem } from '@/types/menu';
|
||||
@ -22,7 +22,7 @@ const emit = defineEmits<{
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
let modal = $ref<InstanceType<typeof MkModal>>();
|
||||
let modal = ref<InstanceType<typeof MkModal>>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -65,7 +65,7 @@
|
||||
</template>
|
||||
|
||||
<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 misskey from 'misskey-js';
|
||||
import insertTextAtCursor from 'insert-text-at-cursor';
|
||||
@ -121,35 +121,35 @@ const emit = defineEmits<{
|
||||
(ev: 'esc'): void;
|
||||
}>();
|
||||
|
||||
const textareaEl = $ref<HTMLTextAreaElement | null>(null);
|
||||
const cwInputEl = $ref<HTMLInputElement | null>(null);
|
||||
const hashtagsInputEl = $ref<HTMLInputElement | null>(null);
|
||||
const visibilityButton = $ref<HTMLElement | null>(null);
|
||||
const textareaEl = ref<HTMLTextAreaElement | null>(null);
|
||||
const cwInputEl = ref<HTMLInputElement | null>(null);
|
||||
const hashtagsInputEl = ref<HTMLInputElement | null>(null);
|
||||
const visibilityButton = ref<HTMLElement | null>(null);
|
||||
|
||||
let posting = $ref(false);
|
||||
let text = $ref(props.initialText ?? '');
|
||||
let files = $ref(props.initialFiles ?? []);
|
||||
let poll = $ref<{
|
||||
let posting = ref(false);
|
||||
let text = ref(props.initialText ?? '');
|
||||
let files = ref(props.initialFiles ?? []);
|
||||
let poll = ref<{
|
||||
choices: string[];
|
||||
multiple: boolean;
|
||||
expiresAt: string | null;
|
||||
expiredAfter: string | null;
|
||||
} | null>(null);
|
||||
let useCw = $ref(false);
|
||||
let showPreview = $ref(false);
|
||||
let cw = $ref<string | null>(null);
|
||||
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 visibleUsers = $ref([]);
|
||||
let useCw = ref(false);
|
||||
let showPreview = ref(false);
|
||||
let cw = ref<string | null>(null);
|
||||
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 visibleUsers = ref([]);
|
||||
if (props.initialVisibleUsers) {
|
||||
props.initialVisibleUsers.forEach(pushVisibleUser);
|
||||
}
|
||||
let autocomplete = $ref(null);
|
||||
let draghover = $ref(false);
|
||||
let quoteId = $ref(null);
|
||||
let hasNotSpecifiedMentions = $ref(false);
|
||||
let recentHashtags = $ref(JSON.parse(localStorage.getItem('hashtags') || '[]'));
|
||||
let imeText = $ref('');
|
||||
let autocomplete = ref(null);
|
||||
let draghover = ref(false);
|
||||
let quoteId = ref(null);
|
||||
let hasNotSpecifiedMentions = ref(false);
|
||||
let recentHashtags = ref(JSON.parse(localStorage.getItem('hashtags') || '[]'));
|
||||
let imeText = ref('');
|
||||
|
||||
const typing = throttle(3000, () => {
|
||||
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}` : '';
|
||||
|
||||
if (props.renote) {
|
||||
@ -171,7 +171,7 @@ const draftKey = $computed((): string => {
|
||||
return key;
|
||||
});
|
||||
|
||||
const placeholder = $computed((): string => {
|
||||
const placeholder = computed((): string => {
|
||||
if (props.renote) {
|
||||
return i18n.ts._postForm.quotePlaceholder;
|
||||
} else if (props.reply) {
|
||||
@ -191,7 +191,7 @@ const placeholder = $computed((): string => {
|
||||
}
|
||||
});
|
||||
|
||||
const submitText = $computed((): string => {
|
||||
const submitText = computed((): string => {
|
||||
return props.renote
|
||||
? i18n.ts.quote
|
||||
: props.reply
|
||||
@ -199,41 +199,41 @@ const submitText = $computed((): string => {
|
||||
: i18n.ts.note;
|
||||
});
|
||||
|
||||
const textLength = $computed((): number => {
|
||||
return length((text + imeText).trim());
|
||||
const textLength = computed((): number => {
|
||||
return length((text.value + imeText.value).trim());
|
||||
});
|
||||
|
||||
const maxTextLength = $computed((): number => {
|
||||
const maxTextLength = computed((): number => {
|
||||
return instance ? instance.maxNoteTextLength : 1000;
|
||||
});
|
||||
|
||||
const canPost = $computed((): boolean => {
|
||||
return !posting &&
|
||||
(1 <= textLength || 1 <= files.length || !!poll || !!props.renote) &&
|
||||
(textLength <= maxTextLength) &&
|
||||
(!poll || poll.choices.length >= 2);
|
||||
const canPost = computed((): boolean => {
|
||||
return !posting.value &&
|
||||
(1 <= textLength.value || 1 <= files.value.length || !!poll.value || !!props.renote) &&
|
||||
(textLength.value <= maxTextLength.value) &&
|
||||
(!poll.value || poll.value.choices.length >= 2);
|
||||
});
|
||||
|
||||
const withHashtags = $computed(defaultStore.makeGetterSetter('postFormWithHashtags'));
|
||||
const hashtags = $computed(defaultStore.makeGetterSetter('postFormHashtags'));
|
||||
const withHashtags = computed(defaultStore.makeGetterSetter('postFormWithHashtags'));
|
||||
const hashtags = computed(defaultStore.makeGetterSetter('postFormHashtags'));
|
||||
|
||||
watch($$(text), () => {
|
||||
watch((text), () => {
|
||||
checkMissingMention();
|
||||
});
|
||||
|
||||
watch($$(visibleUsers), () => {
|
||||
watch((visibleUsers), () => {
|
||||
checkMissingMention();
|
||||
}, {
|
||||
deep: true,
|
||||
});
|
||||
|
||||
if (props.mention) {
|
||||
text = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`;
|
||||
text += ' ';
|
||||
text.value = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`;
|
||||
text.value += ' ';
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -251,20 +251,20 @@ if (props.reply && props.reply.text != null) {
|
||||
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) {
|
||||
visibility = 'public';
|
||||
localOnly = true; // TODO: チャンネルが連合するようになった折には消す
|
||||
visibility.value = 'public';
|
||||
localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す
|
||||
}
|
||||
|
||||
// 公開以外へのリプライ時は元の公開範囲を引き継ぐ
|
||||
if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visibility)) {
|
||||
visibility = props.reply.visibility;
|
||||
visibility.value = props.reply.visibility;
|
||||
if (props.reply.visibility === 'specified') {
|
||||
os.api('users/show', {
|
||||
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) {
|
||||
visibility = 'specified';
|
||||
visibility.value = 'specified';
|
||||
pushVisibleUser(props.specified);
|
||||
}
|
||||
|
||||
// keep cw when reply
|
||||
if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
|
||||
useCw = true;
|
||||
cw = props.reply.cw;
|
||||
useCw.value = true;
|
||||
cw.value = props.reply.cw;
|
||||
}
|
||||
|
||||
function watchForDraft() {
|
||||
watch($$(text), () => saveDraft());
|
||||
watch($$(useCw), () => saveDraft());
|
||||
watch($$(cw), () => saveDraft());
|
||||
watch($$(poll), () => saveDraft());
|
||||
watch($$(files), () => saveDraft(), { deep: true });
|
||||
watch($$(visibility), () => saveDraft());
|
||||
watch($$(localOnly), () => saveDraft());
|
||||
watch((text), () => saveDraft());
|
||||
watch((useCw), () => saveDraft());
|
||||
watch((cw), () => saveDraft());
|
||||
watch((poll), () => saveDraft());
|
||||
watch((files), () => saveDraft(), { deep: true });
|
||||
watch((visibility), () => saveDraft());
|
||||
watch((localOnly), () => saveDraft());
|
||||
}
|
||||
|
||||
function checkMissingMention() {
|
||||
if (visibility === 'specified') {
|
||||
const ast = mfm.parse(text);
|
||||
if (visibility.value === 'specified') {
|
||||
const ast = mfm.parse(text.value);
|
||||
|
||||
for (const x of extractMentions(ast)) {
|
||||
if (!visibleUsers.some(u => (u.username === x.username) && (u.host === x.host))) {
|
||||
hasNotSpecifiedMentions = true;
|
||||
if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) {
|
||||
hasNotSpecifiedMentions.value = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
hasNotSpecifiedMentions = false;
|
||||
hasNotSpecifiedMentions.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function addMissingMention() {
|
||||
const ast = mfm.parse(text);
|
||||
const ast = mfm.parse(text.value);
|
||||
|
||||
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 => {
|
||||
visibleUsers.push(user);
|
||||
visibleUsers.value.push(user);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function togglePoll() {
|
||||
if (poll) {
|
||||
poll = null;
|
||||
if (poll.value) {
|
||||
poll.value = null;
|
||||
} else {
|
||||
poll = {
|
||||
poll.value = {
|
||||
choices: ['', ''],
|
||||
multiple: false,
|
||||
expiresAt: null,
|
||||
@ -341,49 +341,49 @@ function togglePoll() {
|
||||
}
|
||||
|
||||
function addTag(tag: string) {
|
||||
insertTextAtCursor(textareaEl, ` #${tag} `);
|
||||
insertTextAtCursor(textareaEl.value, ` #${tag} `);
|
||||
}
|
||||
|
||||
function focus() {
|
||||
if (textareaEl) {
|
||||
textareaEl.focus();
|
||||
textareaEl.setSelectionRange(textareaEl.value.length, textareaEl.value.length);
|
||||
if (textareaEl.value) {
|
||||
textareaEl.value.focus();
|
||||
textareaEl.value.setSelectionRange(textareaEl.value.value.length, textareaEl.value.value.length);
|
||||
}
|
||||
}
|
||||
|
||||
function chooseFileFrom(ev) {
|
||||
selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => {
|
||||
for (const file of files_) {
|
||||
files.push(file);
|
||||
files.value.push(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function chooseFileFromEnc(ev) {
|
||||
selectFileEnc(ev.currentTarget ?? ev.target, i18n.ts.attachVideoFile).then(files_ => {
|
||||
files.push(files_);
|
||||
files.value.push(files_);
|
||||
});
|
||||
}
|
||||
|
||||
function detachFile(id) {
|
||||
files = files.filter(x => x.id !== id);
|
||||
files.value = files.value.filter(x => x.id !== id);
|
||||
}
|
||||
|
||||
function updateFiles(_files) {
|
||||
files = _files;
|
||||
files.value = _files;
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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')), {
|
||||
currentVisibility: visibility,
|
||||
currentLocalOnly: localOnly,
|
||||
src: visibilityButton,
|
||||
currentVisibility: visibility.value,
|
||||
currentLocalOnly: localOnly.value,
|
||||
src: visibilityButton.value,
|
||||
}, {
|
||||
changeVisibility: v => {
|
||||
visibility = v;
|
||||
visibility.value = v;
|
||||
if (defaultStore.state.rememberNoteVisibility) {
|
||||
defaultStore.set('visibility', visibility);
|
||||
defaultStore.set('visibility', visibility.value);
|
||||
}
|
||||
},
|
||||
changeLocalOnly: v => {
|
||||
localOnly = v;
|
||||
localOnly.value = v;
|
||||
if (defaultStore.state.rememberNoteVisibility) {
|
||||
defaultStore.set('localOnly', localOnly);
|
||||
defaultStore.set('localOnly', localOnly.value);
|
||||
}
|
||||
},
|
||||
}, 'closed');
|
||||
}
|
||||
|
||||
function pushVisibleUser(user) {
|
||||
if (!visibleUsers.some(u => u.username === user.username && u.host === user.host)) {
|
||||
visibleUsers.push(user);
|
||||
if (!visibleUsers.value.some(u => u.username === user.username && u.host === user.host)) {
|
||||
visibleUsers.value.push(user);
|
||||
}
|
||||
}
|
||||
|
||||
@ -426,29 +426,29 @@ function addVisibleUser() {
|
||||
}
|
||||
|
||||
function removeVisibleUser(user) {
|
||||
visibleUsers = erase(user, visibleUsers);
|
||||
visibleUsers.value = erase(user, visibleUsers.value);
|
||||
}
|
||||
|
||||
function clear() {
|
||||
text = '';
|
||||
files = [];
|
||||
poll = null;
|
||||
quoteId = null;
|
||||
text.value = '';
|
||||
files.value = [];
|
||||
poll.value = null;
|
||||
quoteId.value = null;
|
||||
}
|
||||
|
||||
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');
|
||||
typing();
|
||||
}
|
||||
|
||||
function onCompositionUpdate(ev: CompositionEvent) {
|
||||
imeText = ev.data;
|
||||
imeText.value = ev.data;
|
||||
typing();
|
||||
}
|
||||
|
||||
function onCompositionEnd(ev: CompositionEvent) {
|
||||
imeText = '';
|
||||
imeText.value = '';
|
||||
}
|
||||
|
||||
async function onPaste(ev: ClipboardEvent) {
|
||||
@ -464,7 +464,7 @@ async function onPaste(ev: ClipboardEvent) {
|
||||
|
||||
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();
|
||||
|
||||
os.confirm({
|
||||
@ -472,11 +472,11 @@ async function onPaste(ev: ClipboardEvent) {
|
||||
text: i18n.ts.quoteQuestion,
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) {
|
||||
insertTextAtCursor(textareaEl, paste);
|
||||
insertTextAtCursor(textareaEl.value, paste);
|
||||
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_;
|
||||
if (isFile || isDriveFile) {
|
||||
ev.preventDefault();
|
||||
draghover = true;
|
||||
draghover.value = true;
|
||||
switch (ev.dataTransfer.effectAllowed) {
|
||||
case 'all':
|
||||
case 'uninitialized':
|
||||
@ -508,15 +508,15 @@ function onDragover(ev) {
|
||||
}
|
||||
|
||||
function onDragenter(ev) {
|
||||
draghover = true;
|
||||
draghover.value = true;
|
||||
}
|
||||
|
||||
function onDragleave(ev) {
|
||||
draghover = false;
|
||||
draghover.value = false;
|
||||
}
|
||||
|
||||
function onDrop(ev): void {
|
||||
draghover = false;
|
||||
draghover.value = false;
|
||||
|
||||
// ファイルだったら
|
||||
if (ev.dataTransfer.files.length > 0) {
|
||||
@ -529,7 +529,7 @@ function onDrop(ev): void {
|
||||
const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
|
||||
if (driveFile != null && driveFile !== '') {
|
||||
const file = JSON.parse(driveFile);
|
||||
files.push(file);
|
||||
files.value.push(file);
|
||||
ev.preventDefault();
|
||||
}
|
||||
//#endregion
|
||||
@ -538,16 +538,16 @@ function onDrop(ev): void {
|
||||
function saveDraft() {
|
||||
const draftData = JSON.parse(localStorage.getItem('drafts') || '{}');
|
||||
|
||||
draftData[draftKey] = {
|
||||
draftData[draftKey.value] = {
|
||||
updatedAt: new Date(),
|
||||
data: {
|
||||
text: text,
|
||||
useCw: useCw,
|
||||
cw: cw,
|
||||
visibility: visibility,
|
||||
localOnly: localOnly,
|
||||
files: files,
|
||||
poll: poll,
|
||||
text: text.value,
|
||||
useCw: useCw.value,
|
||||
cw: cw.value,
|
||||
visibility: visibility.value,
|
||||
localOnly: localOnly.value,
|
||||
files: files.value,
|
||||
poll: poll.value,
|
||||
},
|
||||
};
|
||||
|
||||
@ -557,27 +557,27 @@ function saveDraft() {
|
||||
function deleteDraft() {
|
||||
const draftData = JSON.parse(localStorage.getItem('drafts') || '{}');
|
||||
|
||||
delete draftData[draftKey];
|
||||
delete draftData[draftKey.value];
|
||||
|
||||
localStorage.setItem('drafts', JSON.stringify(draftData));
|
||||
}
|
||||
|
||||
async function post() {
|
||||
let postData = {
|
||||
text: text === '' ? undefined : text,
|
||||
fileIds: files.length > 0 ? files.map(f => f.id) : undefined,
|
||||
text: text.value === '' ? undefined : text.value,
|
||||
fileIds: files.value.length > 0 ? files.value.map(f => f.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,
|
||||
poll: poll,
|
||||
cw: useCw ? cw || '' : undefined,
|
||||
localOnly: localOnly,
|
||||
visibility: visibility,
|
||||
visibleUserIds: visibility === 'specified' ? visibleUsers.map(u => u.id) : undefined,
|
||||
poll: poll.value,
|
||||
cw: useCw.value ? cw.value || '' : undefined,
|
||||
localOnly: localOnly.value,
|
||||
visibility: visibility.value,
|
||||
visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined,
|
||||
};
|
||||
|
||||
if (withHashtags && hashtags && hashtags.trim() !== '') {
|
||||
const hashtags_ = hashtags.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' ');
|
||||
if (withHashtags.value && hashtags.value && hashtags.value.trim() !== '') {
|
||||
const hashtags_ = hashtags.value.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' ');
|
||||
postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_;
|
||||
}
|
||||
|
||||
@ -590,12 +590,12 @@ async function post() {
|
||||
|
||||
let token = undefined;
|
||||
|
||||
if (postAccount) {
|
||||
if (postAccount.value) {
|
||||
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(() => {
|
||||
clear();
|
||||
nextTick(() => {
|
||||
@ -606,11 +606,11 @@ async function post() {
|
||||
const history = JSON.parse(localStorage.getItem('hashtags') || '[]') as string[];
|
||||
localStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history))));
|
||||
}
|
||||
posting = false;
|
||||
postAccount = null;
|
||||
posting.value = false;
|
||||
postAccount.value = null;
|
||||
});
|
||||
}).catch(err => {
|
||||
posting = false;
|
||||
posting.value = false;
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: err.message + '\n' + (err as any).id,
|
||||
@ -624,12 +624,12 @@ function cancel() {
|
||||
|
||||
function insertMention() {
|
||||
os.selectUser().then(user => {
|
||||
insertTextAtCursor(textareaEl, '@' + Acct.toString(user) + ' ');
|
||||
insertTextAtCursor(textareaEl.value, '@' + Acct.toString(user) + ' ');
|
||||
});
|
||||
}
|
||||
|
||||
async function insertEmoji(ev: MouseEvent) {
|
||||
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl);
|
||||
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl.value);
|
||||
}
|
||||
|
||||
function showActions(ev) {
|
||||
@ -637,26 +637,26 @@ function showActions(ev) {
|
||||
text: action.title,
|
||||
action: () => {
|
||||
action.handler({
|
||||
text: text,
|
||||
text: text.value,
|
||||
}, (key, value) => {
|
||||
if (key === 'text') { text = value; }
|
||||
if (key === 'text') { text.value = value; }
|
||||
});
|
||||
},
|
||||
})), ev.currentTarget ?? ev.target);
|
||||
}
|
||||
|
||||
let postAccount = $ref<misskey.entities.UserDetailed | null>(null);
|
||||
let postAccount = ref<misskey.entities.UserDetailed | null>(null);
|
||||
|
||||
function openAccountMenu(ev: MouseEvent) {
|
||||
openAccountMenu_({
|
||||
withExtraOperation: false,
|
||||
includeCurrentAccount: true,
|
||||
active: postAccount != null ? postAccount.id : $i.id,
|
||||
active: postAccount.value != null ? postAccount.value.id : $i.id,
|
||||
onChoose: (account) => {
|
||||
if (account.id === $i.id) {
|
||||
postAccount = null;
|
||||
postAccount.value = null;
|
||||
} else {
|
||||
postAccount = account;
|
||||
postAccount.value = account;
|
||||
}
|
||||
},
|
||||
}, ev);
|
||||
@ -672,23 +672,23 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
// TODO: detach when unmount
|
||||
new Autocomplete(textareaEl, $$(text));
|
||||
new Autocomplete(cwInputEl, $$(cw));
|
||||
new Autocomplete(hashtagsInputEl, $$(hashtags));
|
||||
new Autocomplete(textareaEl.value, (text));
|
||||
new Autocomplete(cwInputEl.value, (cw));
|
||||
new Autocomplete(hashtagsInputEl.value, (hashtags));
|
||||
|
||||
nextTick(() => {
|
||||
// 書きかけの投稿を復元
|
||||
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) {
|
||||
text = draft.data.text;
|
||||
useCw = draft.data.useCw;
|
||||
cw = draft.data.cw;
|
||||
visibility = draft.data.visibility;
|
||||
localOnly = draft.data.localOnly;
|
||||
files = (draft.data.files || []).filter(draftFile => draftFile);
|
||||
text.value = draft.data.text;
|
||||
useCw.value = draft.data.useCw;
|
||||
cw.value = draft.data.cw;
|
||||
visibility.value = draft.data.visibility;
|
||||
localOnly.value = draft.data.localOnly;
|
||||
files.value = (draft.data.files || []).filter(draftFile => draftFile);
|
||||
if (draft.data.poll) {
|
||||
poll = draft.data.poll;
|
||||
poll.value = draft.data.poll;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -696,21 +696,21 @@ onMounted(() => {
|
||||
// 削除して編集
|
||||
if (props.initialNote) {
|
||||
const init = props.initialNote;
|
||||
text = init.text ? init.text : '';
|
||||
files = init.files;
|
||||
cw = init.cw;
|
||||
useCw = init.cw != null;
|
||||
text.value = init.text ? init.text : '';
|
||||
files.value = init.files;
|
||||
cw.value = init.cw;
|
||||
useCw.value = init.cw != null;
|
||||
if (init.poll) {
|
||||
poll = {
|
||||
poll.value = {
|
||||
choices: init.poll.choices.map(x => x.text),
|
||||
multiple: init.poll.multiple,
|
||||
expiresAt: init.poll.expiresAt,
|
||||
expiredAfter: init.poll.expiredAfter,
|
||||
};
|
||||
}
|
||||
visibility = init.visibility;
|
||||
localOnly = init.localOnly;
|
||||
quoteId = init.renote ? init.renote.id : null;
|
||||
visibility.value = init.visibility;
|
||||
localOnly.value = init.localOnly;
|
||||
quoteId.value = init.renote ? init.renote.id : null;
|
||||
}
|
||||
|
||||
nextTick(() => watchForDraft());
|
||||
|
@ -43,7 +43,7 @@ type reactionUsers = {
|
||||
users: UserLite[];
|
||||
}[]
|
||||
|
||||
const dialog = $ref<InstanceType<typeof MkModalWindow>>();
|
||||
const dialog = ref<InstanceType<typeof MkModalWindow>>();
|
||||
const el = ref<HTMLElement>();
|
||||
const reactions = ref([] as NoteReaction[]);
|
||||
const reactUsers = ref([] as reactionUsers);
|
||||
|
@ -49,7 +49,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
import { defineAsyncComponent, ref, computed } from 'vue';
|
||||
import { toUnicode } from 'punycode/';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/form/input.vue';
|
||||
@ -62,20 +62,20 @@ import { showSuspendedDialog } from '../scripts/show-suspended-dialog';
|
||||
import { instance } from '@/instance';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
let signing = $ref(false);
|
||||
let user = $ref(null);
|
||||
let username = $ref('');
|
||||
let password = $ref('');
|
||||
let token = $ref('');
|
||||
let host = $ref(toUnicode(configHost));
|
||||
let totpLogin = $ref(false);
|
||||
let credential = $ref(null);
|
||||
let challengeData = $ref(null);
|
||||
let queryingKey = $ref(false);
|
||||
let hCaptchaResponse = $ref(null);
|
||||
let reCaptchaResponse = $ref(null);
|
||||
let signing = ref(false);
|
||||
let user = ref(null);
|
||||
let username = ref('');
|
||||
let password = ref('');
|
||||
let token = ref('');
|
||||
let host = ref(toUnicode(configHost));
|
||||
let totpLogin = ref(false);
|
||||
let credential = ref(null);
|
||||
let challengeData = ref(null);
|
||||
let queryingKey = ref(false);
|
||||
let hCaptchaResponse = ref(null);
|
||||
let reCaptchaResponse = ref(null);
|
||||
|
||||
const meta = $computed(() => instance);
|
||||
const meta = computed(() => instance);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'login', v: any): void;
|
||||
@ -101,11 +101,11 @@ const props = defineProps({
|
||||
|
||||
function onUsernameChange() {
|
||||
os.api('users/show', {
|
||||
username: username
|
||||
username: username.value
|
||||
}).then(userResponse => {
|
||||
user = userResponse;
|
||||
user.value = userResponse;
|
||||
}, () => {
|
||||
user = null;
|
||||
user.value = null;
|
||||
});
|
||||
}
|
||||
|
||||
@ -116,11 +116,11 @@ function onLogin(res) {
|
||||
}
|
||||
|
||||
function queryKey() {
|
||||
queryingKey = true;
|
||||
queryingKey.value = true;
|
||||
return navigator.credentials.get({
|
||||
publicKey: {
|
||||
challenge: byteify(challengeData.challenge, 'base64'),
|
||||
allowCredentials: challengeData.securityKeys.map(key => ({
|
||||
challenge: byteify(challengeData.value.challenge, 'base64'),
|
||||
allowCredentials: challengeData.value.securityKeys.map(key => ({
|
||||
id: byteify(key.id, 'hex'),
|
||||
type: 'public-key',
|
||||
transports: ['usb', 'nfc', 'ble', 'internal']
|
||||
@ -128,21 +128,21 @@ function queryKey() {
|
||||
timeout: 60 * 1000
|
||||
}
|
||||
}).catch(() => {
|
||||
queryingKey = false;
|
||||
queryingKey.value = false;
|
||||
return Promise.reject(null);
|
||||
}).then(credential => {
|
||||
queryingKey = false;
|
||||
signing = true;
|
||||
queryingKey.value = false;
|
||||
signing.value = true;
|
||||
return os.api('signin', {
|
||||
username,
|
||||
password,
|
||||
username: username.value,
|
||||
password: password.value,
|
||||
signature: hexify(credential.response.signature),
|
||||
authenticatorData: hexify(credential.response.authenticatorData),
|
||||
clientDataJSON: hexify(credential.response.clientDataJSON),
|
||||
credentialId: credential.id,
|
||||
challengeId: challengeData.challengeId,
|
||||
'hcaptcha-response': hCaptchaResponse,
|
||||
'g-recaptcha-response': reCaptchaResponse,
|
||||
challengeId: challengeData.value.challengeId,
|
||||
'hcaptcha-response': hCaptchaResponse.value,
|
||||
'g-recaptcha-response': reCaptchaResponse.value,
|
||||
});
|
||||
}).then(res => {
|
||||
emit('login', res);
|
||||
@ -153,37 +153,37 @@ function queryKey() {
|
||||
type: 'error',
|
||||
text: i18n.ts.signinFailed
|
||||
});
|
||||
signing = false;
|
||||
signing.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
signing = true;
|
||||
signing.value = true;
|
||||
console.log('submit');
|
||||
if (!totpLogin && user && user.twoFactorEnabled) {
|
||||
if (window.PublicKeyCredential && user.securityKeys) {
|
||||
if (!totpLogin.value && user.value && user.value.twoFactorEnabled) {
|
||||
if (window.PublicKeyCredential && user.value.securityKeys) {
|
||||
os.api('signin', {
|
||||
username,
|
||||
password,
|
||||
'hcaptcha-response': hCaptchaResponse,
|
||||
'g-recaptcha-response': reCaptchaResponse,
|
||||
username: username.value,
|
||||
password: password.value,
|
||||
'hcaptcha-response': hCaptchaResponse.value,
|
||||
'g-recaptcha-response': reCaptchaResponse.value,
|
||||
}).then(res => {
|
||||
totpLogin = true;
|
||||
signing = false;
|
||||
challengeData = res;
|
||||
totpLogin.value = true;
|
||||
signing.value = false;
|
||||
challengeData.value = res;
|
||||
return queryKey();
|
||||
}).catch(loginFailed);
|
||||
} else {
|
||||
totpLogin = true;
|
||||
signing = false;
|
||||
totpLogin.value = true;
|
||||
signing.value = false;
|
||||
}
|
||||
} else {
|
||||
os.api('signin', {
|
||||
username,
|
||||
password,
|
||||
'hcaptcha-response': hCaptchaResponse,
|
||||
'g-recaptcha-response': reCaptchaResponse,
|
||||
token: user && user.twoFactorEnabled ? token : undefined
|
||||
username: username.value,
|
||||
password: password.value,
|
||||
'hcaptcha-response': hCaptchaResponse.value,
|
||||
'g-recaptcha-response': reCaptchaResponse.value,
|
||||
token: user.value && user.value.twoFactorEnabled ? token.value : undefined
|
||||
}).then(res => {
|
||||
emit('login', res);
|
||||
onLogin(res);
|
||||
@ -231,9 +231,9 @@ function loginFailed(err) {
|
||||
}
|
||||
}
|
||||
|
||||
challengeData = null;
|
||||
totpLogin = false;
|
||||
signing = false;
|
||||
challengeData.value = null;
|
||||
totpLogin.value = false;
|
||||
signing.value = false;
|
||||
}
|
||||
|
||||
function resetPassword() {
|
||||
|
@ -13,7 +13,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import MkSignin from '@/components/MkSignin.vue';
|
||||
import XModalWindow from '@/components/MkModalWindow.vue';
|
||||
import { i18n } from '@/i18n';
|
||||
@ -32,15 +32,15 @@ const emit = defineEmits<{
|
||||
(ev: 'cancelled'): void;
|
||||
}>();
|
||||
|
||||
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
||||
const dialog = ref<InstanceType<typeof XModalWindow>>();
|
||||
|
||||
function onClose() {
|
||||
emit('cancelled');
|
||||
dialog.close();
|
||||
dialog.value.close();
|
||||
}
|
||||
|
||||
function onLogin(res) {
|
||||
emit('done', res);
|
||||
dialog.close();
|
||||
dialog.value.close();
|
||||
}
|
||||
</script>
|
||||
|
@ -65,7 +65,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import getPasswordStrength from 'syuilo-password-strength';
|
||||
import { toUnicode } from 'punycode/';
|
||||
import MkButton from './MkButton.vue';
|
||||
@ -91,73 +91,73 @@ const emit = defineEmits<{
|
||||
|
||||
const host = toUnicode(config.host);
|
||||
|
||||
let hcaptcha = $ref();
|
||||
let recaptcha = $ref();
|
||||
let hcaptcha = ref();
|
||||
let recaptcha = ref();
|
||||
|
||||
let username: string = $ref('');
|
||||
let password: string = $ref('');
|
||||
let retypedPassword: string = $ref('');
|
||||
let invitationCode: string = $ref('');
|
||||
let email = $ref('');
|
||||
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 passwordStrength: '' | 'low' | 'medium' | 'high' = $ref('');
|
||||
let passwordRetypeState: null | 'match' | 'not-match' = $ref(null);
|
||||
let submitting: boolean = $ref(false);
|
||||
let ToSAgreement: boolean = $ref(false);
|
||||
let hCaptchaResponse = $ref(null);
|
||||
let reCaptchaResponse = $ref(null);
|
||||
let username: string = ref('');
|
||||
let password: string = ref('');
|
||||
let retypedPassword: string = ref('');
|
||||
let invitationCode: string = ref('');
|
||||
let email = ref('');
|
||||
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 passwordStrength: '' | 'low' | 'medium' | 'high' = ref('');
|
||||
let passwordRetypeState: null | 'match' | 'not-match' = ref(null);
|
||||
let submitting: boolean = ref(false);
|
||||
let ToSAgreement: boolean = ref(false);
|
||||
let hCaptchaResponse = ref(null);
|
||||
let reCaptchaResponse = ref(null);
|
||||
|
||||
const shouldDisableSubmitting = $computed((): boolean => {
|
||||
return submitting ||
|
||||
instance.tosUrl && !ToSAgreement ||
|
||||
instance.enableHcaptcha && !hCaptchaResponse ||
|
||||
instance.enableRecaptcha && !reCaptchaResponse ||
|
||||
passwordRetypeState === 'not-match';
|
||||
const shouldDisableSubmitting = computed((): boolean => {
|
||||
return submitting.value ||
|
||||
instance.tosUrl && !ToSAgreement.value ||
|
||||
instance.enableHcaptcha && !hCaptchaResponse.value ||
|
||||
instance.enableRecaptcha && !reCaptchaResponse.value ||
|
||||
passwordRetypeState.value === 'not-match';
|
||||
});
|
||||
|
||||
function onChangeUsername(): void {
|
||||
if (username === '') {
|
||||
usernameState = null;
|
||||
if (username.value === '') {
|
||||
usernameState.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
const err =
|
||||
!username.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' :
|
||||
username.length < 1 ? 'min-range' :
|
||||
username.length > 20 ? 'max-range' :
|
||||
!username.value.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' :
|
||||
username.value.length < 1 ? 'min-range' :
|
||||
username.value.length > 20 ? 'max-range' :
|
||||
null;
|
||||
|
||||
if (err) {
|
||||
usernameState = err;
|
||||
usernameState.value = err;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
usernameState = 'wait';
|
||||
usernameState.value = 'wait';
|
||||
|
||||
os.api('username/available', {
|
||||
username,
|
||||
username: username.value,
|
||||
}).then(result => {
|
||||
usernameState = result.available ? 'ok' : 'unavailable';
|
||||
usernameState.value = result.available ? 'ok' : 'unavailable';
|
||||
}).catch(() => {
|
||||
usernameState = 'error';
|
||||
usernameState.value = 'error';
|
||||
});
|
||||
}
|
||||
|
||||
function onChangeEmail(): void {
|
||||
if (email === '') {
|
||||
emailState = null;
|
||||
if (email.value === '') {
|
||||
emailState.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
emailState = 'wait';
|
||||
emailState.value = 'wait';
|
||||
|
||||
os.api('email-address/available', {
|
||||
emailAddress: email,
|
||||
emailAddress: email.value,
|
||||
}).then(result => {
|
||||
emailState = result.available ? 'ok' :
|
||||
emailState.value = result.available ? 'ok' :
|
||||
result.reason === 'used' ? 'unavailable:used' :
|
||||
result.reason === 'format' ? 'unavailable:format' :
|
||||
result.reason === 'disposable' ? 'unavailable:disposable' :
|
||||
@ -165,52 +165,52 @@ function onChangeEmail(): void {
|
||||
result.reason === 'smtp' ? 'unavailable:smtp' :
|
||||
'unavailable';
|
||||
}).catch(() => {
|
||||
emailState = 'error';
|
||||
emailState.value = 'error';
|
||||
});
|
||||
}
|
||||
|
||||
function onChangePassword(): void {
|
||||
if (password === '') {
|
||||
passwordStrength = '';
|
||||
if (password.value === '') {
|
||||
passwordStrength.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const strength = getPasswordStrength(password);
|
||||
passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low';
|
||||
const strength = getPasswordStrength(password.value);
|
||||
passwordStrength.value = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low';
|
||||
}
|
||||
|
||||
function onChangePasswordRetype(): void {
|
||||
if (retypedPassword === '') {
|
||||
passwordRetypeState = null;
|
||||
if (retypedPassword.value === '') {
|
||||
passwordRetypeState.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
passwordRetypeState = password === retypedPassword ? 'match' : 'not-match';
|
||||
passwordRetypeState.value = password.value === retypedPassword.value ? 'match' : 'not-match';
|
||||
}
|
||||
|
||||
function onSubmit(): void {
|
||||
if (submitting) return;
|
||||
submitting = true;
|
||||
if (submitting.value) return;
|
||||
submitting.value = true;
|
||||
|
||||
os.api('signup', {
|
||||
username,
|
||||
password,
|
||||
emailAddress: email,
|
||||
invitationCode,
|
||||
'hcaptcha-response': hCaptchaResponse,
|
||||
'g-recaptcha-response': reCaptchaResponse,
|
||||
username: username.value,
|
||||
password: password.value,
|
||||
emailAddress: email.value,
|
||||
invitationCode: invitationCode.value,
|
||||
'hcaptcha-response': hCaptchaResponse.value,
|
||||
'g-recaptcha-response': reCaptchaResponse.value,
|
||||
}).then(() => {
|
||||
if (instance.emailRequiredForSignup) {
|
||||
os.alert({
|
||||
type: 'success',
|
||||
title: i18n.ts._signup.almostThere,
|
||||
text: i18n.t('_signup.emailSent', { email }),
|
||||
text: i18n.t('_signup.emailSent', { email: email.value }),
|
||||
});
|
||||
emit('signupEmailPending');
|
||||
} else {
|
||||
os.api('signin', {
|
||||
username,
|
||||
password,
|
||||
username: username.value,
|
||||
password: password.value,
|
||||
}).then(res => {
|
||||
emit('signup', res);
|
||||
|
||||
@ -220,9 +220,9 @@ function onSubmit(): void {
|
||||
});
|
||||
}
|
||||
}).catch(() => {
|
||||
submitting = false;
|
||||
hcaptcha.reset?.();
|
||||
recaptcha.reset?.();
|
||||
submitting.value = false;
|
||||
hcaptcha.value.reset?.();
|
||||
recaptcha.value.reset?.();
|
||||
|
||||
os.alert({
|
||||
type: 'error',
|
||||
|
@ -17,7 +17,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import XSignup from '@/components/MkSignup.vue';
|
||||
import XModalWindow from '@/components/MkModalWindow.vue';
|
||||
import { i18n } from '@/i18n';
|
||||
@ -33,14 +33,14 @@ const emit = defineEmits<{
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
||||
const dialog = ref<InstanceType<typeof XModalWindow>>();
|
||||
|
||||
function onSignup(res) {
|
||||
emit('done', res);
|
||||
dialog.close();
|
||||
dialog.value.close();
|
||||
}
|
||||
|
||||
function onSignupEmailPending() {
|
||||
dialog.close();
|
||||
dialog.value.close();
|
||||
}
|
||||
</script>
|
||||
|
@ -22,7 +22,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import XMediaList from '@/components/MkMediaList.vue';
|
||||
import XPoll from '@/components/MkPoll.vue';
|
||||
@ -32,7 +32,7 @@ const props = defineProps<{
|
||||
note: misskey.entities.Note;
|
||||
}>();
|
||||
|
||||
const collapsed = $ref(
|
||||
const collapsed = ref(
|
||||
props.note.cw == null && props.note.text != null && (
|
||||
(props.note.text.split('\n').length > 9) ||
|
||||
(props.note.text.length > 500)
|
||||
|
@ -18,13 +18,13 @@ const SAFE_FOR_HTML_ID = 'abcdefghijklmnopqrstuvwxyz';
|
||||
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 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 rootEl = $ref<HTMLElement | null>(null);
|
||||
let canvasEl = $ref<HTMLCanvasElement | null>(null);
|
||||
let tagsEl = $ref<HTMLElement | null>(null);
|
||||
let width = $ref(300);
|
||||
let available = ref(false);
|
||||
let rootEl = ref<HTMLElement | null>(null);
|
||||
let canvasEl = ref<HTMLCanvasElement | null>(null);
|
||||
let tagsEl = ref<HTMLElement | null>(null);
|
||||
let width = ref(300);
|
||||
|
||||
watch($$(available), () => {
|
||||
watch((available), () => {
|
||||
try {
|
||||
window.TagCanvas.Start(idForCanvas, idForTags, {
|
||||
textColour: '#ffffff',
|
||||
@ -47,15 +47,15 @@ watch($$(available), () => {
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
width = rootEl.offsetWidth;
|
||||
width.value = rootEl.value.offsetWidth;
|
||||
|
||||
if (loaded) {
|
||||
available = true;
|
||||
available.value = true;
|
||||
} else {
|
||||
document.head.appendChild(Object.assign(document.createElement('script'), {
|
||||
async: true,
|
||||
src: '/client-assets/tagcanvas.min.js',
|
||||
})).addEventListener('load', () => available = true);
|
||||
})).addEventListener('load', () => available.value = true);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -25,10 +25,10 @@ const emit = defineEmits<{
|
||||
|
||||
provide('inChannel', computed(() => props.src === 'channel'));
|
||||
|
||||
const tlComponent: InstanceType<typeof XNotes> = $ref();
|
||||
const tlComponent: InstanceType<typeof XNotes> = ref();
|
||||
|
||||
const prepend = note => {
|
||||
tlComponent.pagingComponent?.prepend(note);
|
||||
tlComponent.value.pagingComponent?.prepend(note);
|
||||
|
||||
emit('note');
|
||||
|
||||
@ -38,16 +38,16 @@ const prepend = note => {
|
||||
};
|
||||
|
||||
const onUserAdded = () => {
|
||||
tlComponent.pagingComponent?.reload();
|
||||
tlComponent.value.pagingComponent?.reload();
|
||||
};
|
||||
|
||||
const onUserRemoved = () => {
|
||||
tlComponent.pagingComponent?.reload();
|
||||
tlComponent.value.pagingComponent?.reload();
|
||||
};
|
||||
|
||||
const onChangeFollowing = () => {
|
||||
if (!tlComponent.pagingComponent?.backed) {
|
||||
tlComponent.pagingComponent?.reload();
|
||||
if (!tlComponent.value.pagingComponent?.backed) {
|
||||
tlComponent.value.pagingComponent?.reload();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -23,11 +23,11 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const zIndex = os.claimZIndex('high');
|
||||
let showing = $ref(true);
|
||||
let showing = ref(true);
|
||||
|
||||
onMounted(() => {
|
||||
window.setTimeout(() => {
|
||||
showing = false;
|
||||
showing.value = false;
|
||||
}, 4000);
|
||||
});
|
||||
</script>
|
||||
|
@ -29,7 +29,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import { permissions as kinds } from 'misskey-js';
|
||||
import MkInput from './form/input.vue';
|
||||
import MkSwitch from './form/switch.vue';
|
||||
@ -54,37 +54,37 @@ const emit = defineEmits<{
|
||||
(ev: 'done', result: { name: string | null, permissions: string[] }): void;
|
||||
}>();
|
||||
|
||||
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
||||
let name = $ref(props.initialName);
|
||||
let permissions = $ref({});
|
||||
const dialog = ref<InstanceType<typeof XModalWindow>>();
|
||||
let name = ref(props.initialName);
|
||||
let permissions = ref({});
|
||||
|
||||
if (props.initialPermissions) {
|
||||
for (const kind of props.initialPermissions) {
|
||||
permissions[kind] = true;
|
||||
permissions.value[kind] = true;
|
||||
}
|
||||
} else {
|
||||
for (const kind of kinds) {
|
||||
permissions[kind] = false;
|
||||
permissions.value[kind] = false;
|
||||
}
|
||||
}
|
||||
|
||||
function ok(): void {
|
||||
emit('done', {
|
||||
name: name,
|
||||
permissions: Object.keys(permissions).filter(p => permissions[p]),
|
||||
name: name.value,
|
||||
permissions: Object.keys(permissions.value).filter(p => permissions.value[p]),
|
||||
});
|
||||
dialog.close();
|
||||
dialog.value.close();
|
||||
}
|
||||
|
||||
function disableAll(): void {
|
||||
for (const p in permissions) {
|
||||
permissions[p] = false;
|
||||
for (const p in permissions.value) {
|
||||
permissions.value[p] = false;
|
||||
}
|
||||
}
|
||||
|
||||
function enableAll(): void {
|
||||
for (const p in permissions) {
|
||||
permissions[p] = true;
|
||||
for (const p in permissions.value) {
|
||||
permissions.value[p] = true;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -33,7 +33,7 @@
|
||||
</template>
|
||||
|
||||
<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 { i18n } from '@/i18n';
|
||||
import * as os from '@/os';
|
||||
@ -49,33 +49,33 @@ const props = withDefaults(defineProps<{
|
||||
});
|
||||
|
||||
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 attr = self ? 'to' : 'href';
|
||||
const target = self ? null : '_blank';
|
||||
let fetching = $ref(true);
|
||||
let title = $ref<string | null>(null);
|
||||
let description = $ref<string | null>(null);
|
||||
let thumbnail = $ref<string | null>(null);
|
||||
let icon = $ref<string | null>(null);
|
||||
let sitename = $ref<string | null>(null);
|
||||
let player = $ref({
|
||||
let fetching = ref(true);
|
||||
let title = ref<string | null>(null);
|
||||
let description = ref<string | null>(null);
|
||||
let thumbnail = ref<string | null>(null);
|
||||
let icon = ref<string | null>(null);
|
||||
let sitename = ref<string | null>(null);
|
||||
let player = ref({
|
||||
url: null,
|
||||
width: null,
|
||||
height: null,
|
||||
});
|
||||
let playerEnabled = $ref(false);
|
||||
let tweetId = $ref<string | null>(null);
|
||||
let tweetExpanded = $ref(props.detail);
|
||||
let playerEnabled = ref(false);
|
||||
let tweetId = ref<string | null>(null);
|
||||
let tweetExpanded = ref(props.detail);
|
||||
const embedId = `embed${Math.random().toString().replace(/\D/,'')}`;
|
||||
let tweetHeight = $ref(150);
|
||||
let tweetHeight = ref(150);
|
||||
|
||||
const requestUrl = new URL(props.url);
|
||||
|
||||
if (requestUrl.hostname === 'twitter.com' || requestUrl.hostname === 'mobile.twitter.com') {
|
||||
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)')) {
|
||||
@ -89,13 +89,13 @@ requestUrl.hash = '';
|
||||
fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${requestLang}`).then(res => {
|
||||
res.json().then(info => {
|
||||
if (info.url == null) return;
|
||||
title = info.title;
|
||||
description = info.description;
|
||||
thumbnail = info.thumbnail;
|
||||
icon = info.icon;
|
||||
sitename = info.sitename;
|
||||
fetching = false;
|
||||
player = info.player;
|
||||
title.value = info.title;
|
||||
description.value = info.description;
|
||||
thumbnail.value = info.thumbnail;
|
||||
icon.value = info.icon;
|
||||
sitename.value = info.sitename;
|
||||
fetching.value = false;
|
||||
player.value = info.player;
|
||||
});
|
||||
});
|
||||
|
||||
@ -105,7 +105,7 @@ function adjustTweetHeight(message: any) {
|
||||
if (embed?.method !== 'twttr.private.resize') return;
|
||||
if (embed?.id !== embedId) return;
|
||||
const height = embed?.params[0]?.height;
|
||||
if (height) tweetHeight = height;
|
||||
if (height) tweetHeight.value = height;
|
||||
}
|
||||
|
||||
const openPlayer = (): void => {
|
||||
|
@ -7,7 +7,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import MkUrlPreview from '@/components/MkUrlPreview.vue';
|
||||
import * as os from '@/os';
|
||||
|
||||
@ -22,16 +22,16 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const zIndex = os.claimZIndex('middle');
|
||||
let top = $ref(0);
|
||||
let left = $ref(0);
|
||||
let top = ref(0);
|
||||
let left = ref(0);
|
||||
|
||||
onMounted(() => {
|
||||
const rect = props.source.getBoundingClientRect();
|
||||
const x = Math.max((rect.left + (props.source.offsetWidth / 2)) - (300 / 2), 6) + window.pageXOffset;
|
||||
const y = rect.top + props.source.offsetHeight + window.pageYOffset;
|
||||
|
||||
top = y;
|
||||
left = x;
|
||||
top.value = y;
|
||||
left.value = x;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -10,6 +10,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
import * as misskey from 'misskey-js';
|
||||
import MkMiniChart from '@/components/MkMiniChart.vue';
|
||||
import * as os from '@/os';
|
||||
@ -19,12 +21,12 @@ const props = defineProps<{
|
||||
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 => {
|
||||
// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
|
||||
res.inc.splice(0, 1);
|
||||
chartValues = res.inc;
|
||||
chartValues.value = res.inc;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
@ -11,7 +11,7 @@ const props = defineProps<{
|
||||
user: misskey.entities.User;
|
||||
}>();
|
||||
|
||||
const text = $computed(() => {
|
||||
const text = computed(() => {
|
||||
switch (props.user.onlineStatus) {
|
||||
case 'online': return i18n.ts.online;
|
||||
case 'active': return i18n.ts.active;
|
||||
|
@ -34,7 +34,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import * as Acct from 'misskey-js/built/acct';
|
||||
import * as misskey from 'misskey-js';
|
||||
import MkFollowButton from '@/components/MkFollowButton.vue';
|
||||
@ -54,13 +54,13 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const zIndex = os.claimZIndex('middle');
|
||||
let user = $ref<misskey.entities.UserDetailed | null>(null);
|
||||
let top = $ref(0);
|
||||
let left = $ref(0);
|
||||
let user = ref<misskey.entities.UserDetailed | null>(null);
|
||||
let top = ref(0);
|
||||
let left = ref(0);
|
||||
|
||||
onMounted(() => {
|
||||
if (typeof props.q === 'object') {
|
||||
user = props.q;
|
||||
user.value = props.q;
|
||||
} else {
|
||||
const query = props.q.startsWith('@') ?
|
||||
Acct.parse(props.q.substr(1)) :
|
||||
@ -68,7 +68,7 @@ onMounted(() => {
|
||||
|
||||
os.api('users/show', query).then(res => {
|
||||
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 y = rect.top + props.source.offsetHeight + window.pageYOffset;
|
||||
|
||||
top = y;
|
||||
left = x;
|
||||
top.value = y;
|
||||
left.value = x;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -52,7 +52,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onMounted } from 'vue';
|
||||
import { nextTick, onMounted, ref } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import MkInput from '@/components/form/input.vue';
|
||||
import FormSplit from '@/components/form/split.vue';
|
||||
@ -67,50 +67,50 @@ const emit = defineEmits<{
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
let username = $ref('');
|
||||
let host = $ref('');
|
||||
let users: misskey.entities.UserDetailed[] = $ref([]);
|
||||
let recentUsers: misskey.entities.UserDetailed[] = $ref([]);
|
||||
let selected: misskey.entities.UserDetailed | null = $ref(null);
|
||||
let dialogEl = $ref();
|
||||
let username = ref('');
|
||||
let host = ref('');
|
||||
let users: misskey.entities.UserDetailed[] = ref([]);
|
||||
let recentUsers: misskey.entities.UserDetailed[] = ref([]);
|
||||
let selected: misskey.entities.UserDetailed | null = ref(null);
|
||||
let dialogEl = ref();
|
||||
|
||||
const search = () => {
|
||||
if (username === '' && host === '') {
|
||||
users = [];
|
||||
if (username.value === '' && host.value === '') {
|
||||
users.value = [];
|
||||
return;
|
||||
}
|
||||
os.api('users/search-by-username-and-host', {
|
||||
username: username,
|
||||
host: host,
|
||||
username: username.value,
|
||||
host: host.value,
|
||||
limit: 10,
|
||||
detail: false,
|
||||
}).then(_users => {
|
||||
users = _users;
|
||||
users.value = _users;
|
||||
});
|
||||
};
|
||||
|
||||
const ok = () => {
|
||||
if (selected == null) return;
|
||||
emit('ok', selected);
|
||||
dialogEl.close();
|
||||
if (selected.value == null) return;
|
||||
emit('ok', selected.value);
|
||||
dialogEl.value.close();
|
||||
|
||||
// 最近使ったユーザー更新
|
||||
let recents = defaultStore.state.recentlyUsedUsers;
|
||||
recents = recents.filter(x => x !== selected.id);
|
||||
recents.unshift(selected.id);
|
||||
recents = recents.filter(x => x !== selected.value.id);
|
||||
recents.unshift(selected.value.id);
|
||||
defaultStore.set('recentlyUsedUsers', recents.splice(0, 16));
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
emit('cancel');
|
||||
dialogEl.close();
|
||||
dialogEl.value.close();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
os.api('users/show', {
|
||||
userIds: defaultStore.state.recentlyUsedUsers,
|
||||
}).then(users => {
|
||||
recentUsers = users;
|
||||
recentUsers.value = users;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
@ -21,10 +21,10 @@ const props = defineProps<{
|
||||
},
|
||||
}>();
|
||||
|
||||
const specified = $ref<HTMLElement>();
|
||||
const specified = ref<HTMLElement>();
|
||||
|
||||
if (props.note.visibility === 'specified') {
|
||||
useTooltip($$(specified), async (showing) => {
|
||||
useTooltip((specified), async (showing) => {
|
||||
const users = await os.api('users/show', {
|
||||
userIds: props.note.visibleUserIds,
|
||||
limit: 10,
|
||||
@ -34,7 +34,7 @@ if (props.note.visibility === 'specified') {
|
||||
showing,
|
||||
users,
|
||||
count: props.note.visibleUserIds.length,
|
||||
targetElement: specified,
|
||||
targetElement: specified.value,
|
||||
}, {}, 'closed');
|
||||
});
|
||||
}
|
||||
|
@ -43,12 +43,12 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, watch } from 'vue';
|
||||
import { nextTick, watch, ref } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import MkModal from '@/components/MkModal.vue';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
const modal = $ref<InstanceType<typeof MkModal>>();
|
||||
const modal = ref<InstanceType<typeof MkModal>>();
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
currentVisibility: typeof misskey.noteVisibilities[number];
|
||||
@ -63,18 +63,18 @@ const emit = defineEmits<{
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
let v = $ref(props.currentVisibility);
|
||||
let localOnly = $ref(props.currentLocalOnly);
|
||||
let v = ref(props.currentVisibility);
|
||||
let localOnly = ref(props.currentLocalOnly);
|
||||
|
||||
watch($$(localOnly), () => {
|
||||
emit('changeLocalOnly', localOnly);
|
||||
watch((localOnly), () => {
|
||||
emit('changeLocalOnly', localOnly.value);
|
||||
});
|
||||
|
||||
function choose(visibility: typeof misskey.noteVisibilities[number]): void {
|
||||
v = visibility;
|
||||
v.value = visibility;
|
||||
emit('changeVisibility', visibility);
|
||||
nextTick(() => {
|
||||
modal.close();
|
||||
modal.value.close();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
@ -35,7 +35,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, onMounted, provide } from 'vue';
|
||||
import { onBeforeUnmount, onMounted, provide, ref } from 'vue';
|
||||
import contains from '@/scripts/contains';
|
||||
import * as os from '@/os';
|
||||
import { MenuItem } from '@/types/menu';
|
||||
@ -87,17 +87,17 @@ const emit = defineEmits<{
|
||||
|
||||
provide('inWindow', true);
|
||||
|
||||
let rootEl = $ref<HTMLElement | null>();
|
||||
let showing = $ref(true);
|
||||
let rootEl = ref<HTMLElement | null>();
|
||||
let showing = ref(true);
|
||||
let beforeClickedAt = 0;
|
||||
let maximized = $ref(false);
|
||||
let maximized = ref(false);
|
||||
let unMaximizedTop = '';
|
||||
let unMaximizedLeft = '';
|
||||
let unMaximizedWidth = '';
|
||||
let unMaximizedHeight = '';
|
||||
|
||||
function close() {
|
||||
showing = false;
|
||||
showing.value = false;
|
||||
}
|
||||
|
||||
function onKeydown(evt) {
|
||||
@ -116,29 +116,29 @@ function onContextmenu(ev: MouseEvent) {
|
||||
|
||||
// 最前面へ移動
|
||||
function top() {
|
||||
if (rootEl) {
|
||||
rootEl.style.zIndex = os.claimZIndex(props.front ? 'middle' : 'low');
|
||||
if (rootEl.value) {
|
||||
rootEl.value.style.zIndex = os.claimZIndex(props.front ? 'middle' : 'low');
|
||||
}
|
||||
}
|
||||
|
||||
function maximize() {
|
||||
maximized = true;
|
||||
unMaximizedTop = rootEl.style.top;
|
||||
unMaximizedLeft = rootEl.style.left;
|
||||
unMaximizedWidth = rootEl.style.width;
|
||||
unMaximizedHeight = rootEl.style.height;
|
||||
rootEl.style.top = '0';
|
||||
rootEl.style.left = '0';
|
||||
rootEl.style.width = '100%';
|
||||
rootEl.style.height = '100%';
|
||||
maximized.value = true;
|
||||
unMaximizedTop = rootEl.value.style.top;
|
||||
unMaximizedLeft = rootEl.value.style.left;
|
||||
unMaximizedWidth = rootEl.value.style.width;
|
||||
unMaximizedHeight = rootEl.value.style.height;
|
||||
rootEl.value.style.top = '0';
|
||||
rootEl.value.style.left = '0';
|
||||
rootEl.value.style.width = '100%';
|
||||
rootEl.value.style.height = '100%';
|
||||
}
|
||||
|
||||
function unMaximize() {
|
||||
maximized = false;
|
||||
rootEl.style.top = unMaximizedTop;
|
||||
rootEl.style.left = unMaximizedLeft;
|
||||
rootEl.style.width = unMaximizedWidth;
|
||||
rootEl.style.height = unMaximizedHeight;
|
||||
maximized.value = false;
|
||||
rootEl.value.style.top = unMaximizedTop;
|
||||
rootEl.value.style.left = unMaximizedLeft;
|
||||
rootEl.value.style.width = unMaximizedWidth;
|
||||
rootEl.value.style.height = unMaximizedHeight;
|
||||
}
|
||||
|
||||
function onBodyMousedown() {
|
||||
@ -155,7 +155,7 @@ function onHeaderMousedown(evt: MouseEvent) {
|
||||
|
||||
let beforeMaximized = false;
|
||||
|
||||
if (maximized) {
|
||||
if (maximized.value) {
|
||||
beforeMaximized = true;
|
||||
unMaximize();
|
||||
}
|
||||
@ -169,7 +169,7 @@ function onHeaderMousedown(evt: MouseEvent) {
|
||||
|
||||
beforeClickedAt = Date.now();
|
||||
|
||||
const main = rootEl;
|
||||
const main = rootEl.value;
|
||||
if (main == null) return;
|
||||
|
||||
if (!contains(main, document.activeElement)) main.focus();
|
||||
@ -201,8 +201,8 @@ function onHeaderMousedown(evt: MouseEvent) {
|
||||
// 右はみ出し
|
||||
if (moveLeft + windowWidth > browserWidth) moveLeft = browserWidth - windowWidth;
|
||||
|
||||
rootEl.style.left = moveLeft + 'px';
|
||||
rootEl.style.top = moveTop + 'px';
|
||||
rootEl.value.style.left = moveLeft + 'px';
|
||||
rootEl.value.style.top = moveTop + 'px';
|
||||
}
|
||||
|
||||
if (beforeMaximized) {
|
||||
@ -220,7 +220,7 @@ function onHeaderMousedown(evt: MouseEvent) {
|
||||
|
||||
// 上ハンドル掴み時
|
||||
function onTopHandleMousedown(evt) {
|
||||
const main = rootEl;
|
||||
const main = rootEl.value;
|
||||
|
||||
const base = evt.clientY;
|
||||
const height = parseInt(getComputedStyle(main, '').height, 10);
|
||||
@ -246,7 +246,7 @@ function onTopHandleMousedown(evt) {
|
||||
|
||||
// 右ハンドル掴み時
|
||||
function onRightHandleMousedown(evt) {
|
||||
const main = rootEl;
|
||||
const main = rootEl.value;
|
||||
|
||||
const base = evt.clientX;
|
||||
const width = parseInt(getComputedStyle(main, '').width, 10);
|
||||
@ -270,7 +270,7 @@ function onRightHandleMousedown(evt) {
|
||||
|
||||
// 下ハンドル掴み時
|
||||
function onBottomHandleMousedown(evt) {
|
||||
const main = rootEl;
|
||||
const main = rootEl.value;
|
||||
|
||||
const base = evt.clientY;
|
||||
const height = parseInt(getComputedStyle(main, '').height, 10);
|
||||
@ -294,7 +294,7 @@ function onBottomHandleMousedown(evt) {
|
||||
|
||||
// 左ハンドル掴み時
|
||||
function onLeftHandleMousedown(evt) {
|
||||
const main = rootEl;
|
||||
const main = rootEl.value;
|
||||
|
||||
const base = evt.clientX;
|
||||
const width = parseInt(getComputedStyle(main, '').width, 10);
|
||||
@ -345,27 +345,27 @@ function onBottomLeftHandleMousedown(evt) {
|
||||
// 高さを適用
|
||||
function applyTransformHeight(height) {
|
||||
if (height > window.innerHeight) height = window.innerHeight;
|
||||
rootEl.style.height = height + 'px';
|
||||
rootEl.value.style.height = height + 'px';
|
||||
}
|
||||
|
||||
// 幅を適用
|
||||
function applyTransformWidth(width) {
|
||||
if (width > window.innerWidth) width = window.innerWidth;
|
||||
rootEl.style.width = width + 'px';
|
||||
rootEl.value.style.width = width + 'px';
|
||||
}
|
||||
|
||||
// Y座標を適用
|
||||
function applyTransformTop(top) {
|
||||
rootEl.style.top = top + 'px';
|
||||
rootEl.value.style.top = top + 'px';
|
||||
}
|
||||
|
||||
// X座標を適用
|
||||
function applyTransformLeft(left) {
|
||||
rootEl.style.left = left + 'px';
|
||||
rootEl.value.style.left = left + 'px';
|
||||
}
|
||||
|
||||
function onBrowserResize() {
|
||||
const main = rootEl;
|
||||
const main = rootEl.value;
|
||||
const position = main.getBoundingClientRect();
|
||||
const browserWidth = window.innerWidth;
|
||||
const browserHeight = window.innerHeight;
|
||||
@ -381,8 +381,8 @@ onMounted(() => {
|
||||
if (props.initialWidth) applyTransformWidth(props.initialWidth);
|
||||
if (props.initialHeight) applyTransformHeight(props.initialHeight);
|
||||
|
||||
applyTransformTop((window.innerHeight / 2) - (rootEl.offsetHeight / 2));
|
||||
applyTransformLeft((window.innerWidth / 2) - (rootEl.offsetWidth / 2));
|
||||
applyTransformTop((window.innerHeight / 2) - (rootEl.value.offsetHeight / 2));
|
||||
applyTransformLeft((window.innerWidth / 2) - (rootEl.value.offsetWidth / 2));
|
||||
|
||||
// 他のウィンドウ内のボタンなどを押してこのウィンドウが開かれた場合、親が最前面になろうとするのでそれに隠されないようにする
|
||||
top();
|
||||
|
@ -18,6 +18,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
import XWindow from '@/components/MkWindow.vue';
|
||||
import { lang } from '@/config';
|
||||
|
||||
@ -27,9 +29,9 @@ const props = defineProps<{
|
||||
|
||||
const requestUrl = new URL(props.url);
|
||||
|
||||
let fetching = $ref(true);
|
||||
let title = $ref<string | null>(null);
|
||||
let player = $ref({
|
||||
let fetching = ref(true);
|
||||
let title = ref<string | null>(null);
|
||||
let player = ref({
|
||||
url: null,
|
||||
width: null,
|
||||
height: null,
|
||||
@ -38,13 +40,13 @@ let player = $ref({
|
||||
const requestLang = (lang ?? 'ja-JP').replace('ja-KS', 'ja-JP');
|
||||
|
||||
const ytFetch = (): void => {
|
||||
fetching = true;
|
||||
fetching.value = true;
|
||||
fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${requestLang}`).then(res => {
|
||||
res.json().then(info => {
|
||||
if (info.url == null) return;
|
||||
title = info.title;
|
||||
fetching = false;
|
||||
player = info.player;
|
||||
title.value = info.title;
|
||||
fetching.value = false;
|
||||
player.value = info.player;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -21,7 +21,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, Ref } from 'vue';
|
||||
import { toRefs, Ref, ref } from 'vue';
|
||||
import * as os from '@/os';
|
||||
import Ripple from '@/components/MkRipple.vue';
|
||||
import { i18n } from '@/i18n';
|
||||
@ -35,16 +35,16 @@ const emit = defineEmits<{
|
||||
(ev: 'update:modelValue', v: boolean): void;
|
||||
}>();
|
||||
|
||||
let button = $ref<HTMLElement>();
|
||||
let button = ref<HTMLElement>();
|
||||
const checked = toRefs(props).modelValue;
|
||||
const toggle = () => {
|
||||
if (props.disabled) return;
|
||||
emit('update:modelValue', !checked.value);
|
||||
|
||||
if (!checked.value) {
|
||||
const rect = button.getBoundingClientRect();
|
||||
const x = rect.left + (button.offsetWidth / 2);
|
||||
const y = rect.top + (button.offsetHeight / 2);
|
||||
const rect = button.value.getBoundingClientRect();
|
||||
const x = rect.left + (button.value.offsetWidth / 2);
|
||||
const y = rect.top + (button.value.offsetHeight / 2);
|
||||
os.popup(Ripple, { x, y, particle: false }, {}, 'end');
|
||||
}
|
||||
};
|
||||
|
@ -20,19 +20,21 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
defaultOpen: boolean;
|
||||
}>(), {
|
||||
defaultOpen: false,
|
||||
});
|
||||
|
||||
let opened = $ref(props.defaultOpen);
|
||||
let openedAtLeastOnce = $ref(props.defaultOpen);
|
||||
let opened = ref(props.defaultOpen);
|
||||
let openedAtLeastOnce = ref(props.defaultOpen);
|
||||
|
||||
const toggle = () => {
|
||||
opened = !opened;
|
||||
if (opened) {
|
||||
openedAtLeastOnce = true;
|
||||
opened.value = !opened.value;
|
||||
if (opened.value) {
|
||||
openedAtLeastOnce.value = true;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -19,7 +19,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: any;
|
||||
@ -31,7 +31,7 @@ const emit = defineEmits<{
|
||||
(ev: 'update:modelValue', value: any): void;
|
||||
}>();
|
||||
|
||||
let checked = $computed(() => props.modelValue === props.value);
|
||||
let checked = computed(() => props.modelValue === props.value);
|
||||
|
||||
function toggle(): void {
|
||||
if (props.disabled) return;
|
||||
|
@ -21,7 +21,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, Ref } from 'vue';
|
||||
import { toRefs, Ref, ref } from 'vue';
|
||||
import * as os from '@/os';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
@ -34,7 +34,7 @@ const emit = defineEmits<{
|
||||
(ev: 'update:modelValue', v: boolean): void;
|
||||
}>();
|
||||
|
||||
let button = $ref<HTMLElement>();
|
||||
let button = ref<HTMLElement>();
|
||||
const checked = toRefs(props).modelValue;
|
||||
const toggle = () => {
|
||||
if (props.disabled) return;
|
||||
|
@ -5,7 +5,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { inject } from 'vue';
|
||||
import { inject, computed } from 'vue';
|
||||
import * as os from '@/os';
|
||||
import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||
import { url } from '@/config';
|
||||
@ -24,7 +24,7 @@ const props = withDefaults(defineProps<{
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const active = $computed(() => {
|
||||
const active = computed(() => {
|
||||
if (props.activeClass == null) return false;
|
||||
const resolved = router.resolve(props.to);
|
||||
if (resolved == null) return false;
|
||||
|
@ -10,7 +10,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, watch } from 'vue';
|
||||
import { onMounted, watch, ref } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
|
||||
import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash';
|
||||
@ -48,10 +48,10 @@ function onClick(ev: MouseEvent) {
|
||||
emit('click', ev);
|
||||
}
|
||||
|
||||
let color = $ref();
|
||||
let color = ref();
|
||||
|
||||
watch(() => props.user.avatarBlurhash, () => {
|
||||
color = extractAvgColorFromBlurhash(props.user.avatarBlurhash);
|
||||
color.value = extractAvgColorFromBlurhash(props.user.avatarBlurhash);
|
||||
}, {
|
||||
immediate: true,
|
||||
});
|
||||
|
@ -78,21 +78,21 @@ const metadata = injectPageMetadata();
|
||||
const hideTitle = inject('shouldOmitHeaderTitle', false);
|
||||
const thin_ = props.thin || inject('shouldHeaderThin', false);
|
||||
|
||||
const el = $ref<HTMLElement | null>(null);
|
||||
const el = ref<HTMLElement | null>(null);
|
||||
const tabRefs = {};
|
||||
const tabHighlightEl = $ref<HTMLElement | null>(null);
|
||||
const tabHighlightEl = ref<HTMLElement | null>(null);
|
||||
const bg = ref(null);
|
||||
let narrow = $ref(false);
|
||||
let narrow = ref(false);
|
||||
const height = ref(0);
|
||||
const hasTabs = $computed(() => props.tabs && props.tabs.length > 0);
|
||||
const hasActions = $computed(() => props.actions && props.actions.length > 0);
|
||||
const show = $computed(() => {
|
||||
return !hideTitle || hasTabs || hasActions;
|
||||
const hasTabs = computed(() => props.tabs && props.tabs.length > 0);
|
||||
const hasActions = computed(() => props.actions && props.actions.length > 0);
|
||||
const show = computed(() => {
|
||||
return !hideTitle || hasTabs.value || hasActions.value;
|
||||
});
|
||||
|
||||
const showTabsPopup = (ev: MouseEvent) => {
|
||||
if (!hasTabs) return;
|
||||
if (!narrow) return;
|
||||
if (!hasTabs.value) return;
|
||||
if (!narrow.value) return;
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
const menu = props.tabs.map(tab => ({
|
||||
@ -111,7 +111,7 @@ const preventDrag = (ev: TouchEvent) => {
|
||||
};
|
||||
|
||||
const onClick = () => {
|
||||
scrollToTop(el, { behavior: 'smooth' });
|
||||
scrollToTop(el.value, { behavior: 'smooth' });
|
||||
};
|
||||
|
||||
function onTabMousedown(tab: Tab, ev: MouseEvent): void {
|
||||
@ -148,27 +148,27 @@ onMounted(() => {
|
||||
watch(() => [props.tab, props.tabs], () => {
|
||||
nextTick(() => {
|
||||
const tabEl = tabRefs[props.tab];
|
||||
if (tabEl && tabHighlightEl) {
|
||||
if (tabEl && tabHighlightEl.value) {
|
||||
// offsetWidth や offsetLeft は少数を丸めてしまうため getBoundingClientRect を使う必要がある
|
||||
// https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4
|
||||
const parentRect = tabEl.parentElement.getBoundingClientRect();
|
||||
const rect = tabEl.getBoundingClientRect();
|
||||
tabHighlightEl.style.width = rect.width + 'px';
|
||||
tabHighlightEl.style.left = (rect.left - parentRect.left) + 'px';
|
||||
tabHighlightEl.value.style.width = rect.width + 'px';
|
||||
tabHighlightEl.value.style.left = (rect.left - parentRect.left) + 'px';
|
||||
}
|
||||
});
|
||||
}, {
|
||||
immediate: true,
|
||||
});
|
||||
|
||||
if (el && el.parentElement) {
|
||||
narrow = el.parentElement.offsetWidth < 500;
|
||||
if (el.value && el.value.parentElement) {
|
||||
narrow.value = el.value.parentElement.offsetWidth < 500;
|
||||
ro = new ResizeObserver((entries, observer) => {
|
||||
if (el.parentElement && document.body.contains(el)) {
|
||||
narrow = el.parentElement.offsetWidth < 500;
|
||||
if (el.value.parentElement && document.body.contains(el.value)) {
|
||||
narrow.value = el.value.parentElement.offsetWidth < 500;
|
||||
}
|
||||
});
|
||||
ro.observe(el.parentElement);
|
||||
ro.observe(el.value.parentElement);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -21,21 +21,21 @@ const props = withDefaults(defineProps<{
|
||||
});
|
||||
|
||||
let ro: ResizeObserver;
|
||||
let root = $ref<HTMLElement>();
|
||||
let content = $ref<HTMLElement>();
|
||||
let margin = $ref(0);
|
||||
let root = ref<HTMLElement>();
|
||||
let content = ref<HTMLElement>();
|
||||
let margin = ref(0);
|
||||
const shouldSpacerMin = inject('shouldSpacerMin', false);
|
||||
|
||||
const adjust = (rect: { width: number; height: number; }) => {
|
||||
if (shouldSpacerMin || deviceKind === 'smartphone') {
|
||||
margin = props.marginMin;
|
||||
margin.value = props.marginMin;
|
||||
return;
|
||||
}
|
||||
|
||||
if (rect.width > (props.contentMax ?? 0) || (rect.width > 360 && window.innerWidth > 400)) {
|
||||
margin = props.marginMax;
|
||||
margin.value = props.marginMax;
|
||||
} else {
|
||||
margin = props.marginMin;
|
||||
margin.value = props.marginMin;
|
||||
}
|
||||
};
|
||||
|
||||
@ -48,14 +48,14 @@ onMounted(() => {
|
||||
});
|
||||
*/
|
||||
adjust({
|
||||
width: root!.offsetWidth,
|
||||
height: root!.offsetHeight,
|
||||
width: root.value!.offsetWidth,
|
||||
height: root.value!.offsetHeight,
|
||||
});
|
||||
});
|
||||
ro.observe(root!);
|
||||
ro.observe(root.value!);
|
||||
|
||||
if (props.contentMax) {
|
||||
content!.style.maxWidth = `${props.contentMax}px`;
|
||||
content.value!.style.maxWidth = `${props.contentMax}px`;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -18,18 +18,18 @@ const CURRENT_STICKY_TOP = 'CURRENT_STICKY_TOP';
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted, provide, inject, Ref, ref, watch } from 'vue';
|
||||
|
||||
const rootEl = $ref<HTMLElement>();
|
||||
const headerEl = $ref<HTMLElement>();
|
||||
const bodyEl = $ref<HTMLElement>();
|
||||
const rootEl = ref<HTMLElement>();
|
||||
const headerEl = ref<HTMLElement>();
|
||||
const bodyEl = ref<HTMLElement>();
|
||||
|
||||
let headerHeight = $ref<string | undefined>();
|
||||
let childStickyTop = $ref(0);
|
||||
let headerHeight = ref<string | undefined>();
|
||||
let childStickyTop = ref(0);
|
||||
const parentStickyTop = inject<Ref<number>>(CURRENT_STICKY_TOP, ref(0));
|
||||
provide(CURRENT_STICKY_TOP, $$(childStickyTop));
|
||||
provide(CURRENT_STICKY_TOP, (childStickyTop));
|
||||
|
||||
const calc = () => {
|
||||
childStickyTop = parentStickyTop.value + headerEl.offsetHeight;
|
||||
headerHeight = headerEl.offsetHeight.toString();
|
||||
childStickyTop.value = parentStickyTop.value + headerEl.value.offsetHeight;
|
||||
headerHeight.value = headerEl.value.offsetHeight.toString();
|
||||
};
|
||||
|
||||
const observer = new ResizeObserver(() => {
|
||||
@ -43,17 +43,17 @@ onMounted(() => {
|
||||
|
||||
watch(parentStickyTop, calc);
|
||||
|
||||
watch($$(childStickyTop), () => {
|
||||
bodyEl.style.setProperty('--stickyTop', `${childStickyTop}px`);
|
||||
watch((childStickyTop), () => {
|
||||
bodyEl.value.style.setProperty('--stickyTop', `${childStickyTop.value}px`);
|
||||
}, {
|
||||
immediate: true,
|
||||
});
|
||||
|
||||
headerEl.style.position = 'sticky';
|
||||
headerEl.style.top = 'var(--stickyTop, 0)';
|
||||
headerEl.style.zIndex = '1000';
|
||||
headerEl.value.style.position = 'sticky';
|
||||
headerEl.value.style.top = 'var(--stickyTop, 0)';
|
||||
headerEl.value.style.zIndex = '1000';
|
||||
|
||||
observer.observe(headerEl);
|
||||
observer.observe(headerEl.value);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
|
@ -7,7 +7,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onUnmounted } from 'vue';
|
||||
import { onUnmounted, shallowRef, computed } from 'vue';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
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 absolute = _time.toLocaleString();
|
||||
|
||||
let now = $shallowRef(new Date());
|
||||
const relative = $computed(() => {
|
||||
const ago = (now.getTime() - _time.getTime()) / 1000/*ms*/;
|
||||
let now = shallowRef(new Date());
|
||||
const relative = computed(() => {
|
||||
const ago = (now.value.getTime() - _time.getTime()) / 1000/*ms*/;
|
||||
return (
|
||||
ago >= 86400 ? _time.toLocaleDateString() :
|
||||
ago >= 3600 ? i18n.t('_ago.hoursAgo', { n: Math.round(ago / 3600).toString() }) :
|
||||
@ -32,8 +32,8 @@ const relative = $computed(() => {
|
||||
i18n.ts._ago.future);
|
||||
});
|
||||
|
||||
const allRelative = $computed(() => {
|
||||
const ago = (now.getTime() - _time.getTime()) / 1000/*ms*/;
|
||||
const allRelative = computed(() => {
|
||||
const ago = (now.value.getTime() - _time.getTime()) / 1000/*ms*/;
|
||||
return (
|
||||
ago >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago / 31536000).toString() }) :
|
||||
ago >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago / 2592000).toString() }) :
|
||||
@ -48,7 +48,7 @@ const allRelative = $computed(() => {
|
||||
|
||||
function tick() {
|
||||
// TODO: パフォーマンス向上のため、このコンポーネントが画面内に表示されている場合のみ更新する
|
||||
now = new Date();
|
||||
now.value = new Date();
|
||||
|
||||
tickId = window.setTimeout(() => {
|
||||
window.requestAnimationFrame(tick);
|
||||
|
@ -11,7 +11,7 @@
|
||||
</template>
|
||||
|
||||
<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 { defaultStore } from '@/store';
|
||||
|
||||
@ -41,16 +41,16 @@ function resolveNested(current: Resolved, d = 0): Resolved | null {
|
||||
}
|
||||
|
||||
const current = resolveNested(router.current)!;
|
||||
let currentPageComponent = $shallowRef(current.route.component);
|
||||
let currentPageProps = $ref(current.props);
|
||||
let key = $ref(current.route.path + JSON.stringify(Object.fromEntries(current.props)));
|
||||
let currentPageComponent = shallowRef(current.route.component);
|
||||
let currentPageProps = ref(current.props);
|
||||
let key = ref(current.route.path + JSON.stringify(Object.fromEntries(current.props)));
|
||||
|
||||
function onChange({ resolved, key: newKey }) {
|
||||
const current = resolveNested(resolved);
|
||||
if (current == null) return;
|
||||
currentPageComponent = current.route.component;
|
||||
currentPageProps = current.props;
|
||||
key = current.route.path + JSON.stringify(Object.fromEntries(current.props));
|
||||
currentPageComponent.value = current.route.component;
|
||||
currentPageProps.value = current.props;
|
||||
key.value = current.route.path + JSON.stringify(Object.fromEntries(current.props));
|
||||
}
|
||||
|
||||
router.addListener('change', onChange);
|
||||
|
@ -18,7 +18,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import { version } from '@/config';
|
||||
@ -32,29 +32,29 @@ const props = withDefaults(defineProps<{
|
||||
}>(), {
|
||||
});
|
||||
|
||||
let loaded = $ref(false);
|
||||
let serverIsDead = $ref(false);
|
||||
let meta = $ref<misskey.entities.LiteInstanceMetadata | null>(null);
|
||||
let loaded = ref(false);
|
||||
let serverIsDead = ref(false);
|
||||
let meta = ref<misskey.entities.LiteInstanceMetadata | null>(null);
|
||||
|
||||
os.api('meta', {
|
||||
detail: false,
|
||||
}).then(res => {
|
||||
loaded = true;
|
||||
serverIsDead = false;
|
||||
meta = res;
|
||||
loaded.value = true;
|
||||
serverIsDead.value = false;
|
||||
meta.value = res;
|
||||
localStorage.setItem('v', res.version);
|
||||
}, () => {
|
||||
loaded = true;
|
||||
serverIsDead = true;
|
||||
loaded.value = true;
|
||||
serverIsDead.value = true;
|
||||
});
|
||||
|
||||
function reload() {
|
||||
unisonReload();
|
||||
}
|
||||
|
||||
const headerActions = $computed(() => []);
|
||||
const headerActions = computed(() => []);
|
||||
|
||||
const headerTabs = $computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePageMetadata({
|
||||
title: i18n.ts.error,
|
||||
|
@ -63,7 +63,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onBeforeUnmount } from 'vue';
|
||||
import { nextTick, onBeforeUnmount, ref, computed } from 'vue';
|
||||
import { version } from '@/config';
|
||||
import FormLink from '@/components/form/link.vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
@ -157,15 +157,15 @@ const patrons = [
|
||||
];
|
||||
|
||||
let easterEggReady = false;
|
||||
let easterEggEmojis = $ref([]);
|
||||
let easterEggEngine = $ref(null);
|
||||
const containerEl = $ref<HTMLElement>();
|
||||
let easterEggEmojis = ref([]);
|
||||
let easterEggEngine = ref(null);
|
||||
const containerEl = ref<HTMLElement>();
|
||||
|
||||
function iconLoaded() {
|
||||
const emojis = defaultStore.state.reactions;
|
||||
const containerWidth = containerEl.offsetWidth;
|
||||
const containerWidth = containerEl.value.offsetWidth;
|
||||
for (let i = 0; i < 32; i++) {
|
||||
easterEggEmojis.push({
|
||||
easterEggEmojis.value.push({
|
||||
id: i.toString(),
|
||||
top: -(128 + (Math.random() * 256)),
|
||||
left: (Math.random() * containerWidth),
|
||||
@ -181,7 +181,7 @@ function iconLoaded() {
|
||||
function gravity() {
|
||||
if (!easterEggReady) return;
|
||||
easterEggReady = false;
|
||||
easterEggEngine = physics(containerEl);
|
||||
easterEggEngine.value = physics(containerEl.value);
|
||||
}
|
||||
|
||||
function iLoveMisskey() {
|
||||
@ -192,14 +192,14 @@ function iLoveMisskey() {
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (easterEggEngine) {
|
||||
easterEggEngine.stop();
|
||||
if (easterEggEngine.value) {
|
||||
easterEggEngine.value.stop();
|
||||
}
|
||||
});
|
||||
|
||||
const headerActions = $computed(() => []);
|
||||
const headerActions = computed(() => []);
|
||||
|
||||
const headerTabs = $computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePageMetadata({
|
||||
title: i18n.ts.aboutMisskey,
|
||||
|
@ -47,7 +47,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/form/input.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 { i18n } from '@/i18n';
|
||||
|
||||
let host = $ref('');
|
||||
let state = $ref('federating');
|
||||
let sort = $ref('+pubSub');
|
||||
let host = ref('');
|
||||
let state = ref('federating');
|
||||
let sort = ref('+pubSub');
|
||||
const pagination = {
|
||||
endpoint: 'federation/instances' as const,
|
||||
limit: 10,
|
||||
offsetMode: true,
|
||||
params: computed(() => ({
|
||||
sort: sort,
|
||||
host: host !== '' ? host : null,
|
||||
sort: sort.value,
|
||||
host: host.value !== '' ? host.value : null,
|
||||
...(
|
||||
state === 'federating' ? { federating: true } :
|
||||
state === 'subscribing' ? { subscribing: true } :
|
||||
state === 'publishing' ? { publishing: true } :
|
||||
state === 'suspended' ? { suspended: true } :
|
||||
state === 'blocked' ? { blocked: true } :
|
||||
state === 'notResponding' ? { notResponding: true } :
|
||||
state.value === 'federating' ? { federating: true } :
|
||||
state.value === 'subscribing' ? { subscribing: true } :
|
||||
state.value === 'publishing' ? { publishing: true } :
|
||||
state.value === 'suspended' ? { suspended: true } :
|
||||
state.value === 'blocked' ? { blocked: true } :
|
||||
state.value === 'notResponding' ? { notResponding: true } :
|
||||
{}),
|
||||
})),
|
||||
};
|
||||
|
@ -103,17 +103,17 @@ const props = withDefaults(defineProps<{
|
||||
initialTab: 'overview',
|
||||
});
|
||||
|
||||
let stats = $ref(null);
|
||||
let tab = $ref(props.initialTab);
|
||||
let stats = ref(null);
|
||||
let tab = ref(props.initialTab);
|
||||
|
||||
const initStats = () => os.api('stats', {
|
||||
}).then((res) => {
|
||||
stats = res;
|
||||
stats.value = res;
|
||||
});
|
||||
|
||||
const headerActions = $computed(() => []);
|
||||
const headerActions = computed(() => []);
|
||||
|
||||
const headerTabs = $computed(() => [{
|
||||
const headerTabs = computed(() => [{
|
||||
key: 'overview',
|
||||
title: i18n.ts.overview,
|
||||
}, {
|
||||
|
@ -62,7 +62,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkSwitch from '@/components/form/switch.vue';
|
||||
import MkObjectView from '@/components/MkObjectView.vue';
|
||||
@ -78,19 +78,19 @@ import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
import { acct } from '@/filters/user';
|
||||
import { iAmAdmin, iAmModerator } from '@/account';
|
||||
|
||||
let tab = $ref('overview');
|
||||
let file: any = $ref(null);
|
||||
let info: any = $ref(null);
|
||||
let isSensitive: boolean = $ref(false);
|
||||
let tab = ref('overview');
|
||||
let file: any = ref(null);
|
||||
let info: any = ref(null);
|
||||
let isSensitive: boolean = ref(false);
|
||||
|
||||
const props = defineProps<{
|
||||
fileId: string,
|
||||
}>();
|
||||
|
||||
async function fetch() {
|
||||
file = await os.api('drive/files/show', { fileId: props.fileId });
|
||||
info = await os.api('admin/drive/show-file', { fileId: props.fileId });
|
||||
isSensitive = file.isSensitive;
|
||||
file.value = await os.api('drive/files/show', { fileId: props.fileId });
|
||||
info.value = await os.api('admin/drive/show-file', { fileId: props.fileId });
|
||||
isSensitive.value = file.value.isSensitive;
|
||||
}
|
||||
|
||||
fetch();
|
||||
@ -98,29 +98,29 @@ fetch();
|
||||
async function del() {
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'warning',
|
||||
text: i18n.t('removeAreYouSure', { x: file.name }),
|
||||
text: i18n.t('removeAreYouSure', { x: file.value.name }),
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
os.apiWithDialog('drive/files/delete', {
|
||||
fileId: file.id,
|
||||
fileId: file.value.id,
|
||||
});
|
||||
}
|
||||
|
||||
async function toggleIsSensitive(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,
|
||||
icon: 'fas fa-external-link-alt',
|
||||
handler: () => {
|
||||
window.open(file.url, '_blank');
|
||||
window.open(file.value.url, '_blank');
|
||||
},
|
||||
}]);
|
||||
|
||||
const headerTabs = $computed(() => [{
|
||||
const headerTabs = computed(() => [{
|
||||
key: 'overview',
|
||||
title: i18n.ts.overview,
|
||||
icon: 'fas fa-info-circle',
|
||||
@ -135,7 +135,7 @@ const headerTabs = $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',
|
||||
})));
|
||||
</script>
|
||||
|
@ -66,7 +66,7 @@ const metadata = injectPageMetadata();
|
||||
|
||||
const el = ref<HTMLElement>(null);
|
||||
const tabRefs = {};
|
||||
const tabHighlightEl = $ref<HTMLElement | null>(null);
|
||||
const tabHighlightEl = ref<HTMLElement | null>(null);
|
||||
const bg = ref(null);
|
||||
const height = ref(0);
|
||||
const hasTabs = computed(() => {
|
||||
@ -128,13 +128,13 @@ onMounted(() => {
|
||||
watch(() => [props.tab, props.tabs], () => {
|
||||
nextTick(() => {
|
||||
const tabEl = tabRefs[props.tab];
|
||||
if (tabEl && tabHighlightEl) {
|
||||
if (tabEl && tabHighlightEl.value) {
|
||||
// offsetWidth や offsetLeft は少数を丸めてしまうため getBoundingClientRect を使う必要がある
|
||||
// https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4
|
||||
const parentRect = tabEl.parentElement.getBoundingClientRect();
|
||||
const rect = tabEl.getBoundingClientRect();
|
||||
tabHighlightEl.style.width = rect.width + 'px';
|
||||
tabHighlightEl.style.left = (rect.left - parentRect.left) + 'px';
|
||||
tabHighlightEl.value.style.width = rect.width + 'px';
|
||||
tabHighlightEl.value.style.left = (rect.left - parentRect.left) + 'px';
|
||||
}
|
||||
});
|
||||
}, {
|
||||
|
@ -47,7 +47,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import XHeader from './_header_.vue';
|
||||
import MkInput from '@/components/form/input.vue';
|
||||
@ -58,31 +58,31 @@ import * as os from '@/os';
|
||||
import { i18n } from '@/i18n';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
|
||||
let reports = $ref<InstanceType<typeof MkPagination>>();
|
||||
let reports = ref<InstanceType<typeof MkPagination>>();
|
||||
|
||||
let state = $ref('unresolved');
|
||||
let reporterOrigin = $ref('combined');
|
||||
let targetUserOrigin = $ref('combined');
|
||||
let searchUsername = $ref('');
|
||||
let searchHost = $ref('');
|
||||
let state = ref('unresolved');
|
||||
let reporterOrigin = ref('combined');
|
||||
let targetUserOrigin = ref('combined');
|
||||
let searchUsername = ref('');
|
||||
let searchHost = ref('');
|
||||
|
||||
const pagination = {
|
||||
endpoint: 'admin/abuse-user-reports' as const,
|
||||
limit: 10,
|
||||
params: computed(() => ({
|
||||
state,
|
||||
reporterOrigin,
|
||||
targetUserOrigin,
|
||||
state: state.value,
|
||||
reporterOrigin: reporterOrigin.value,
|
||||
targetUserOrigin: targetUserOrigin.value,
|
||||
})),
|
||||
};
|
||||
|
||||
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({
|
||||
title: i18n.ts.abuseReports,
|
||||
|
@ -47,7 +47,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import XHeader from './_header_.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/form/input.vue';
|
||||
@ -58,14 +58,14 @@ import * as os from '@/os';
|
||||
import { i18n } from '@/i18n';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
|
||||
let ads: any[] = $ref([]);
|
||||
let ads: any[] = ref([]);
|
||||
|
||||
os.api('admin/ad/list').then(adsResponse => {
|
||||
ads = adsResponse;
|
||||
ads.value = adsResponse;
|
||||
});
|
||||
|
||||
function add() {
|
||||
ads.unshift({
|
||||
ads.value.unshift({
|
||||
id: null,
|
||||
memo: '',
|
||||
place: 'square',
|
||||
@ -83,7 +83,7 @@ function remove(ad) {
|
||||
text: i18n.t('removeAreYouSure', { x: ad.url }),
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) return;
|
||||
ads = ads.filter(x => x !== ad);
|
||||
ads.value = ads.value.filter(x => x !== ad);
|
||||
os.apiWithDialog('admin/ad/delete', {
|
||||
id: ad.id,
|
||||
});
|
||||
@ -104,14 +104,14 @@ function save(ad) {
|
||||
}
|
||||
}
|
||||
|
||||
const headerActions = $computed(() => [{
|
||||
const headerActions = computed(() => [{
|
||||
asFullButton: true,
|
||||
icon: 'fas fa-plus',
|
||||
text: i18n.ts.add,
|
||||
handler: add,
|
||||
}]);
|
||||
|
||||
const headerTabs = $computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePageMetadata({
|
||||
title: i18n.ts.ads,
|
||||
|
@ -27,7 +27,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import XHeader from './_header_.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/form/input.vue';
|
||||
@ -36,14 +36,14 @@ import * as os from '@/os';
|
||||
import { i18n } from '@/i18n';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
|
||||
let announcements: any[] = $ref([]);
|
||||
let announcements: any[] = ref([]);
|
||||
|
||||
os.api('admin/announcements/list').then(announcementResponse => {
|
||||
announcements = announcementResponse;
|
||||
announcements.value = announcementResponse;
|
||||
});
|
||||
|
||||
function add() {
|
||||
announcements.unshift({
|
||||
announcements.value.unshift({
|
||||
id: null,
|
||||
title: '',
|
||||
text: '',
|
||||
@ -57,7 +57,7 @@ function remove(announcement) {
|
||||
text: i18n.t('removeAreYouSure', { x: announcement.title }),
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) return;
|
||||
announcements = announcements.filter(x => x !== announcement);
|
||||
announcements.value = announcements.value.filter(x => x !== announcement);
|
||||
os.api('admin/announcements/delete', announcement);
|
||||
});
|
||||
}
|
||||
@ -90,14 +90,14 @@ function save(announcement) {
|
||||
}
|
||||
}
|
||||
|
||||
const headerActions = $computed(() => [{
|
||||
const headerActions = computed(() => [{
|
||||
asFullButton: true,
|
||||
icon: 'fas fa-plus',
|
||||
text: i18n.ts.add,
|
||||
handler: add,
|
||||
}]);
|
||||
|
||||
const headerTabs = $computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePageMetadata({
|
||||
title: i18n.ts.announcements,
|
||||
|
@ -44,7 +44,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
import { defineAsyncComponent, ref } from 'vue';
|
||||
import FormRadios from '@/components/form/radios.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
import FormButton from '@/components/MkButton.vue';
|
||||
@ -56,30 +56,30 @@ import { i18n } from '@/i18n';
|
||||
|
||||
const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue'));
|
||||
|
||||
let provider = $ref(null);
|
||||
let hcaptchaSiteKey: string | null = $ref(null);
|
||||
let hcaptchaSecretKey: string | null = $ref(null);
|
||||
let recaptchaSiteKey: string | null = $ref(null);
|
||||
let recaptchaSecretKey: string | null = $ref(null);
|
||||
let provider = ref(null);
|
||||
let hcaptchaSiteKey: string | null = ref(null);
|
||||
let hcaptchaSecretKey: string | null = ref(null);
|
||||
let recaptchaSiteKey: string | null = ref(null);
|
||||
let recaptchaSecretKey: string | null = ref(null);
|
||||
|
||||
async function init() {
|
||||
const meta = await os.api('admin/meta');
|
||||
hcaptchaSiteKey = meta.hcaptchaSiteKey;
|
||||
hcaptchaSecretKey = meta.hcaptchaSecretKey;
|
||||
recaptchaSiteKey = meta.recaptchaSiteKey;
|
||||
recaptchaSecretKey = meta.recaptchaSecretKey;
|
||||
hcaptchaSiteKey.value = meta.hcaptchaSiteKey;
|
||||
hcaptchaSecretKey.value = meta.hcaptchaSecretKey;
|
||||
recaptchaSiteKey.value = meta.recaptchaSiteKey;
|
||||
recaptchaSecretKey.value = meta.recaptchaSecretKey;
|
||||
|
||||
provider = meta.enableHcaptcha ? 'hcaptcha' : meta.enableRecaptcha ? 'recaptcha' : null;
|
||||
provider.value = meta.enableHcaptcha ? 'hcaptcha' : meta.enableRecaptcha ? 'recaptcha' : null;
|
||||
}
|
||||
|
||||
function save() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
enableHcaptcha: provider === 'hcaptcha',
|
||||
hcaptchaSiteKey,
|
||||
hcaptchaSecretKey,
|
||||
enableRecaptcha: provider === 'recaptcha',
|
||||
recaptchaSiteKey,
|
||||
recaptchaSecretKey,
|
||||
enableHcaptcha: provider.value === 'hcaptcha',
|
||||
hcaptchaSiteKey: hcaptchaSiteKey.value,
|
||||
hcaptchaSecretKey: hcaptchaSecretKey.value,
|
||||
enableRecaptcha: provider.value === 'recaptcha',
|
||||
recaptchaSiteKey: recaptchaSiteKey.value,
|
||||
recaptchaSecretKey: recaptchaSecretKey.value,
|
||||
}).then(() => {
|
||||
fetchInstance();
|
||||
});
|
||||
|
@ -11,7 +11,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||
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 headerActions = $computed(() => []);
|
||||
const headerActions = computed(() => []);
|
||||
|
||||
const headerTabs = $computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePageMetadata({
|
||||
title: i18n.ts.database,
|
||||
|
@ -46,7 +46,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import XHeader from './_header_.vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
@ -59,23 +59,23 @@ import { fetchInstance, instance } from '@/instance';
|
||||
import { i18n } from '@/i18n';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
|
||||
let enableEmail: boolean = $ref(false);
|
||||
let email: any = $ref(null);
|
||||
let smtpSecure: boolean = $ref(false);
|
||||
let smtpHost: string = $ref('');
|
||||
let smtpPort: number = $ref(0);
|
||||
let smtpUser: string = $ref('');
|
||||
let smtpPass: string = $ref('');
|
||||
let enableEmail: boolean = ref(false);
|
||||
let email: any = ref(null);
|
||||
let smtpSecure: boolean = ref(false);
|
||||
let smtpHost: string = ref('');
|
||||
let smtpPort: number = ref(0);
|
||||
let smtpUser: string = ref('');
|
||||
let smtpPass: string = ref('');
|
||||
|
||||
async function init() {
|
||||
const meta = await os.api('admin/meta');
|
||||
enableEmail = meta.enableEmail;
|
||||
email = meta.email;
|
||||
smtpSecure = meta.smtpSecure;
|
||||
smtpHost = meta.smtpHost;
|
||||
smtpPort = meta.smtpPort;
|
||||
smtpUser = meta.smtpUser;
|
||||
smtpPass = meta.smtpPass;
|
||||
enableEmail.value = meta.enableEmail;
|
||||
email.value = meta.email;
|
||||
smtpSecure.value = meta.smtpSecure;
|
||||
smtpHost.value = meta.smtpHost;
|
||||
smtpPort.value = meta.smtpPort;
|
||||
smtpUser.value = meta.smtpUser;
|
||||
smtpPass.value = meta.smtpPass;
|
||||
}
|
||||
|
||||
async function testEmail() {
|
||||
@ -94,19 +94,19 @@ async function testEmail() {
|
||||
|
||||
function save() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
enableEmail,
|
||||
email,
|
||||
smtpSecure,
|
||||
smtpHost,
|
||||
smtpPort,
|
||||
smtpUser,
|
||||
smtpPass,
|
||||
enableEmail: enableEmail.value,
|
||||
email: email.value,
|
||||
smtpSecure: smtpSecure.value,
|
||||
smtpHost: smtpHost.value,
|
||||
smtpPort: smtpPort.value,
|
||||
smtpUser: smtpUser.value,
|
||||
smtpPass: smtpPass.value,
|
||||
}).then(() => {
|
||||
fetchInstance();
|
||||
});
|
||||
}
|
||||
|
||||
const headerActions = $computed(() => [{
|
||||
const headerActions = computed(() => [{
|
||||
asFullButton: true,
|
||||
text: i18n.ts.testEmail,
|
||||
handler: testEmail,
|
||||
@ -117,7 +117,7 @@ const headerActions = $computed(() => [{
|
||||
handler: save,
|
||||
}]);
|
||||
|
||||
const headerTabs = $computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePageMetadata({
|
||||
title: i18n.ts.emailServer,
|
||||
|
@ -29,7 +29,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import XModalWindow from '@/components/MkModalWindow.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/form/input.vue';
|
||||
@ -42,11 +42,11 @@ const props = defineProps<{
|
||||
emoji: any,
|
||||
}>();
|
||||
|
||||
let dialog = $ref(null);
|
||||
let name: string = $ref(props.emoji.name);
|
||||
let category: string = $ref(props.emoji.category);
|
||||
let aliases: string = $ref(props.emoji.aliases.join(' '));
|
||||
let categories: string[] = $ref(emojiCategories);
|
||||
let dialog = ref(null);
|
||||
let name: string = ref(props.emoji.name);
|
||||
let category: string = ref(props.emoji.category);
|
||||
let aliases: string = ref(props.emoji.aliases.join(' '));
|
||||
let categories: string[] = ref(emojiCategories);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'done', v: { deleted?: boolean, updated?: any }): void,
|
||||
@ -60,27 +60,27 @@ function ok() {
|
||||
async function update() {
|
||||
await os.apiWithDialog('admin/emoji/update', {
|
||||
id: props.emoji.id,
|
||||
name,
|
||||
category,
|
||||
aliases: aliases.split(' '),
|
||||
name: name.value,
|
||||
category: category.value,
|
||||
aliases: aliases.value.split(' '),
|
||||
});
|
||||
|
||||
emit('done', {
|
||||
updated: {
|
||||
id: props.emoji.id,
|
||||
name,
|
||||
category,
|
||||
aliases: aliases.split(' '),
|
||||
name: name.value,
|
||||
category: category.value,
|
||||
aliases: aliases.value.split(' '),
|
||||
},
|
||||
});
|
||||
|
||||
dialog.close();
|
||||
dialog.value.close();
|
||||
}
|
||||
|
||||
async function del() {
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'warning',
|
||||
text: i18n.t('removeAreYouSure', { x: name }),
|
||||
text: i18n.t('removeAreYouSure', { x: name.value }),
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
@ -90,7 +90,7 @@ async function del() {
|
||||
emit('done', {
|
||||
deleted: true,
|
||||
});
|
||||
dialog.close();
|
||||
dialog.value.close();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
@ -134,7 +134,7 @@ definePageMetadata(computed(() => ({
|
||||
})));
|
||||
|
||||
const tab = ref('string');
|
||||
const headerTabs = $computed(() => [{
|
||||
const headerTabs = computed(() => [{
|
||||
key: 'string',
|
||||
title: i18n.ts._simkey.emojiNormal,
|
||||
}, {
|
||||
|
@ -271,7 +271,7 @@ const delBulk = async () => {
|
||||
emojisPaginationComponent.value.reload();
|
||||
};
|
||||
|
||||
const headerActions = $computed(() => [{
|
||||
const headerActions = computed(() => [{
|
||||
asFullButton: true,
|
||||
icon: 'fas fa-plus',
|
||||
text: i18n.ts.addEmoji,
|
||||
@ -281,7 +281,7 @@ const headerActions = $computed(() => [{
|
||||
handler: menu,
|
||||
}]);
|
||||
|
||||
const headerTabs = $computed(() => [{
|
||||
const headerTabs = computed(() => [{
|
||||
key: 'local',
|
||||
title: i18n.ts.local,
|
||||
}, {
|
||||
|
@ -33,7 +33,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, defineAsyncComponent } from 'vue';
|
||||
import { computed, defineAsyncComponent, ref } from 'vue';
|
||||
import * as Acct from 'misskey-js/built/acct';
|
||||
import XHeader from './_header_.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
@ -45,19 +45,19 @@ import * as os from '@/os';
|
||||
import { i18n } from '@/i18n';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
|
||||
let origin = $ref('local');
|
||||
let type = $ref(null);
|
||||
let searchHost = $ref('');
|
||||
let userId = $ref('');
|
||||
let viewMode = $ref('grid');
|
||||
let origin = ref('local');
|
||||
let type = ref(null);
|
||||
let searchHost = ref('');
|
||||
let userId = ref('');
|
||||
let viewMode = ref('grid');
|
||||
const pagination = {
|
||||
endpoint: 'admin/drive/files' as const,
|
||||
limit: 10,
|
||||
params: computed(() => ({
|
||||
type: (type && type !== '') ? type : null,
|
||||
userId: (userId && userId !== '') ? userId : null,
|
||||
origin: origin,
|
||||
hostname: (searchHost && searchHost !== '') ? searchHost : null,
|
||||
type: (type.value && type.value !== '') ? type.value : null,
|
||||
userId: (userId.value && userId.value !== '') ? userId.value : null,
|
||||
origin: origin.value,
|
||||
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,
|
||||
icon: 'fas fa-search',
|
||||
handler: find,
|
||||
@ -105,7 +105,7 @@ const headerActions = $computed(() => [{
|
||||
handler: clear,
|
||||
}]);
|
||||
|
||||
const headerTabs = $computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePageMetadata(computed(() => ({
|
||||
title: i18n.ts.files,
|
||||
|
@ -23,7 +23,7 @@
|
||||
</template>
|
||||
|
||||
<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 { i18n } from '@/i18n';
|
||||
import MkSuperMenu from '@/components/MkSuperMenu.vue';
|
||||
@ -47,32 +47,32 @@ const indexInfo = {
|
||||
|
||||
provide('shouldOmitHeaderTitle', false);
|
||||
|
||||
let INFO = $ref(indexInfo);
|
||||
let childInfo = $ref(null);
|
||||
let narrow = $ref(false);
|
||||
let view = $ref(null);
|
||||
let el = $ref(null);
|
||||
let pageProps = $ref({});
|
||||
let INFO = ref(indexInfo);
|
||||
let childInfo = ref(null);
|
||||
let narrow = ref(false);
|
||||
let view = ref(null);
|
||||
let el = ref(null);
|
||||
let pageProps = ref({});
|
||||
let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
|
||||
let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha;
|
||||
let noEmailServer = !instance.enableEmail;
|
||||
let thereIsUnresolvedAbuseReport = $ref(false);
|
||||
let currentPage = $computed(() => router.currentRef.value.child);
|
||||
let thereIsUnresolvedAbuseReport = ref(false);
|
||||
let currentPage = computed(() => router.currentRef.value.child);
|
||||
|
||||
os.api('admin/abuse-user-reports', {
|
||||
state: 'unresolved',
|
||||
limit: 1,
|
||||
}).then(reports => {
|
||||
if (reports.length > 0) thereIsUnresolvedAbuseReport = true;
|
||||
if (reports.length > 0) thereIsUnresolvedAbuseReport.value = true;
|
||||
});
|
||||
|
||||
const NARROW_THRESHOLD = 600;
|
||||
const ro = new ResizeObserver((entries, observer) => {
|
||||
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,
|
||||
items: [{
|
||||
type: 'button',
|
||||
@ -91,52 +91,52 @@ const menuDef = $computed(() => [{
|
||||
icon: 'fas fa-tachometer-alt',
|
||||
text: i18n.ts.dashboard,
|
||||
to: '/admin/overview',
|
||||
active: currentPage?.route.name === 'overview',
|
||||
active: currentPage.value?.route.name === 'overview',
|
||||
}, {
|
||||
icon: 'fas fa-users',
|
||||
text: i18n.ts.users,
|
||||
to: '/admin/users',
|
||||
active: currentPage?.route.name === 'users',
|
||||
active: currentPage.value?.route.name === 'users',
|
||||
}, {
|
||||
icon: 'fas fa-laugh',
|
||||
text: i18n.ts.customEmojis,
|
||||
to: '/admin/emojis',
|
||||
active: currentPage?.route.name === 'emojis',
|
||||
active: currentPage.value?.route.name === 'emojis',
|
||||
}, {
|
||||
icon: 'fas fa-kiss-wink-heart',
|
||||
text: i18n.ts._simkey.emojiGen,
|
||||
to: '/admin/emojigen',
|
||||
active: currentPage?.route.name === 'emojigen',
|
||||
active: currentPage.value?.route.name === 'emojigen',
|
||||
}, {
|
||||
icon: 'fas fa-globe',
|
||||
text: i18n.ts.federation,
|
||||
to: '/about#federation',
|
||||
active: currentPage?.route.name === 'federation',
|
||||
active: currentPage.value?.route.name === 'federation',
|
||||
}, {
|
||||
icon: 'fas fa-clipboard-list',
|
||||
text: i18n.ts.jobQueue,
|
||||
to: '/admin/queue',
|
||||
active: currentPage?.route.name === 'queue',
|
||||
active: currentPage.value?.route.name === 'queue',
|
||||
}, {
|
||||
icon: 'fas fa-cloud',
|
||||
text: i18n.ts.files,
|
||||
to: '/admin/files',
|
||||
active: currentPage?.route.name === 'files',
|
||||
active: currentPage.value?.route.name === 'files',
|
||||
}, {
|
||||
icon: 'fas fa-broadcast-tower',
|
||||
text: i18n.ts.announcements,
|
||||
to: '/admin/announcements',
|
||||
active: currentPage?.route.name === 'announcements',
|
||||
active: currentPage.value?.route.name === 'announcements',
|
||||
}, {
|
||||
icon: 'fas fa-audio-description',
|
||||
text: i18n.ts.ads,
|
||||
to: '/admin/ads',
|
||||
active: currentPage?.route.name === 'ads',
|
||||
active: currentPage.value?.route.name === 'ads',
|
||||
}, {
|
||||
icon: 'fas fa-exclamation-circle',
|
||||
text: i18n.ts.abuseReports,
|
||||
to: '/admin/abuses',
|
||||
active: currentPage?.route.name === 'abuses',
|
||||
active: currentPage.value?.route.name === 'abuses',
|
||||
}],
|
||||
}, {
|
||||
title: i18n.ts.settings,
|
||||
@ -144,47 +144,47 @@ const menuDef = $computed(() => [{
|
||||
icon: 'fas fa-cog',
|
||||
text: i18n.ts.general,
|
||||
to: '/admin/settings',
|
||||
active: currentPage?.route.name === 'settings',
|
||||
active: currentPage.value?.route.name === 'settings',
|
||||
}] : []), ...($i?.isAdmin ? [{
|
||||
icon: 'fas fa-envelope',
|
||||
text: i18n.ts.emailServer,
|
||||
to: '/admin/email-settings',
|
||||
active: currentPage?.route.name === 'email-settings',
|
||||
active: currentPage.value?.route.name === 'email-settings',
|
||||
}] : []), ...($i?.isAdmin ? [{
|
||||
icon: 'fas fa-cloud',
|
||||
text: i18n.ts.objectStorage,
|
||||
to: '/admin/object-storage',
|
||||
active: currentPage?.route.name === 'object-storage',
|
||||
active: currentPage.value?.route.name === 'object-storage',
|
||||
}] : []), ...($i?.isAdmin ? [{
|
||||
icon: 'fas fa-lock',
|
||||
text: i18n.ts.security,
|
||||
to: '/admin/security',
|
||||
active: currentPage?.route.name === 'security',
|
||||
active: currentPage.value?.route.name === 'security',
|
||||
}] : []), {
|
||||
icon: 'fas fa-globe',
|
||||
text: i18n.ts.relays,
|
||||
to: '/admin/relays',
|
||||
active: currentPage?.route.name === 'relays',
|
||||
active: currentPage.value?.route.name === 'relays',
|
||||
}, ...($i?.isAdmin ? [{
|
||||
icon: 'fas fa-share-alt',
|
||||
text: i18n.ts.integration,
|
||||
to: '/admin/integrations',
|
||||
active: currentPage?.route.name === 'integrations',
|
||||
active: currentPage.value?.route.name === 'integrations',
|
||||
}] : []), ...($i?.isAdmin ? [{
|
||||
icon: 'fas fa-ban',
|
||||
text: i18n.ts.instanceBlocking,
|
||||
to: '/admin/instance-block',
|
||||
active: currentPage?.route.name === 'instance-block',
|
||||
active: currentPage.value?.route.name === 'instance-block',
|
||||
}] : []), ...($i?.isAdmin ? [{
|
||||
icon: 'fas fa-ghost',
|
||||
text: i18n.ts.proxyAccount,
|
||||
to: '/admin/proxy-account',
|
||||
active: currentPage?.route.name === 'proxy-account',
|
||||
active: currentPage.value?.route.name === 'proxy-account',
|
||||
}] : []), ...($i?.isAdmin ? [{
|
||||
icon: 'fas fa-cogs',
|
||||
text: i18n.ts.other,
|
||||
to: '/admin/other-settings',
|
||||
active: currentPage?.route.name === 'other-settings',
|
||||
active: currentPage.value?.route.name === 'other-settings',
|
||||
}] : [])],
|
||||
}, {
|
||||
title: i18n.ts.info,
|
||||
@ -192,21 +192,21 @@ const menuDef = $computed(() => [{
|
||||
icon: 'fas fa-database',
|
||||
text: i18n.ts.database,
|
||||
to: '/admin/database',
|
||||
active: currentPage?.route.name === 'database',
|
||||
active: currentPage.value?.route.name === 'database',
|
||||
}],
|
||||
}]);
|
||||
|
||||
watch(narrow, () => {
|
||||
if (currentPage?.route.name == null && !narrow) {
|
||||
watch(narrow.value, () => {
|
||||
if (currentPage.value?.route.name == null && !narrow.value) {
|
||||
router.push('/admin/overview');
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
ro.observe(el);
|
||||
ro.observe(el.value);
|
||||
|
||||
narrow = el.offsetWidth < NARROW_THRESHOLD;
|
||||
if (currentPage?.route.name == null && !narrow) {
|
||||
narrow.value = el.value.offsetWidth < NARROW_THRESHOLD;
|
||||
if (currentPage.value?.route.name == null && !narrow.value) {
|
||||
router.push('/admin/overview');
|
||||
}
|
||||
});
|
||||
@ -217,9 +217,9 @@ onUnmounted(() => {
|
||||
|
||||
provideMetadataReceiver((info) => {
|
||||
if (info == null) {
|
||||
childInfo = null;
|
||||
childInfo.value = null;
|
||||
} else {
|
||||
childInfo = info;
|
||||
childInfo.value = info;
|
||||
}
|
||||
});
|
||||
|
||||
@ -265,11 +265,11 @@ const lookup = (ev) => {
|
||||
}], ev.currentTarget ?? ev.target);
|
||||
};
|
||||
|
||||
const headerActions = $computed(() => []);
|
||||
const headerActions = computed(() => []);
|
||||
|
||||
const headerTabs = $computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePageMetadata(INFO);
|
||||
definePageMetadata(INFO.value);
|
||||
|
||||
defineExpose({
|
||||
header: {
|
||||
|
@ -15,7 +15,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import XHeader from './_header_.vue';
|
||||
import FormButton from '@/components/MkButton.vue';
|
||||
import FormTextarea from '@/components/form/textarea.vue';
|
||||
@ -25,24 +25,24 @@ import { fetchInstance } from '@/instance';
|
||||
import { i18n } from '@/i18n';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
|
||||
let blockedHosts: string = $ref('');
|
||||
let blockedHosts: string = ref('');
|
||||
|
||||
async function init() {
|
||||
const meta = await os.api('admin/meta');
|
||||
blockedHosts = meta.blockedHosts.join('\n');
|
||||
blockedHosts.value = meta.blockedHosts.join('\n');
|
||||
}
|
||||
|
||||
function save() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
blockedHosts: blockedHosts.split('\n') || [],
|
||||
blockedHosts: blockedHosts.value.split('\n') || [],
|
||||
}).then(() => {
|
||||
fetchInstance();
|
||||
});
|
||||
}
|
||||
|
||||
const headerActions = $computed(() => []);
|
||||
const headerActions = computed(() => []);
|
||||
|
||||
const headerTabs = $computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePageMetadata({
|
||||
title: i18n.ts.instanceBlocking,
|
||||
|
@ -25,7 +25,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
import FormButton from '@/components/MkButton.vue';
|
||||
@ -35,24 +35,24 @@ import * as os from '@/os';
|
||||
import { fetchInstance } from '@/instance';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
let uri: string = $ref('');
|
||||
let enableDiscordIntegration: boolean = $ref(false);
|
||||
let discordClientId: string | null = $ref(null);
|
||||
let discordClientSecret: string | null = $ref(null);
|
||||
let uri: string = ref('');
|
||||
let enableDiscordIntegration: boolean = ref(false);
|
||||
let discordClientId: string | null = ref(null);
|
||||
let discordClientSecret: string | null = ref(null);
|
||||
|
||||
async function init() {
|
||||
const meta = await os.api('admin/meta');
|
||||
uri = meta.uri;
|
||||
enableDiscordIntegration = meta.enableDiscordIntegration;
|
||||
discordClientId = meta.discordClientId;
|
||||
discordClientSecret = meta.discordClientSecret;
|
||||
uri.value = meta.uri;
|
||||
enableDiscordIntegration.value = meta.enableDiscordIntegration;
|
||||
discordClientId.value = meta.discordClientId;
|
||||
discordClientSecret.value = meta.discordClientSecret;
|
||||
}
|
||||
|
||||
function save() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
enableDiscordIntegration,
|
||||
discordClientId,
|
||||
discordClientSecret,
|
||||
enableDiscordIntegration: enableDiscordIntegration.value,
|
||||
discordClientId: discordClientId.value,
|
||||
discordClientSecret: discordClientSecret.value,
|
||||
}).then(() => {
|
||||
fetchInstance();
|
||||
});
|
||||
|
@ -25,7 +25,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
import FormButton from '@/components/MkButton.vue';
|
||||
@ -35,24 +35,24 @@ import * as os from '@/os';
|
||||
import { fetchInstance } from '@/instance';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
let uri: string = $ref('');
|
||||
let enableGithubIntegration: boolean = $ref(false);
|
||||
let githubClientId: string | null = $ref(null);
|
||||
let githubClientSecret: string | null = $ref(null);
|
||||
let uri: string = ref('');
|
||||
let enableGithubIntegration: boolean = ref(false);
|
||||
let githubClientId: string | null = ref(null);
|
||||
let githubClientSecret: string | null = ref(null);
|
||||
|
||||
async function init() {
|
||||
const meta = await os.api('admin/meta');
|
||||
uri = meta.uri;
|
||||
enableGithubIntegration = meta.enableGithubIntegration;
|
||||
githubClientId = meta.githubClientId;
|
||||
githubClientSecret = meta.githubClientSecret;
|
||||
uri.value = meta.uri;
|
||||
enableGithubIntegration.value = meta.enableGithubIntegration;
|
||||
githubClientId.value = meta.githubClientId;
|
||||
githubClientSecret.value = meta.githubClientSecret;
|
||||
}
|
||||
|
||||
function save() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
enableGithubIntegration,
|
||||
githubClientId,
|
||||
githubClientSecret,
|
||||
enableGithubIntegration: enableGithubIntegration.value,
|
||||
githubClientId: githubClientId.value,
|
||||
githubClientSecret: githubClientSecret.value,
|
||||
}).then(() => {
|
||||
fetchInstance();
|
||||
});
|
||||
|
@ -25,7 +25,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineComponent } from 'vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
import FormButton from '@/components/MkButton.vue';
|
||||
@ -35,24 +35,24 @@ import * as os from '@/os';
|
||||
import { fetchInstance } from '@/instance';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
let uri: string = $ref('');
|
||||
let enableTwitterIntegration: boolean = $ref(false);
|
||||
let twitterConsumerKey: string | null = $ref(null);
|
||||
let twitterConsumerSecret: string | null = $ref(null);
|
||||
let uri: string = ref('');
|
||||
let enableTwitterIntegration: boolean = ref(false);
|
||||
let twitterConsumerKey: string | null = ref(null);
|
||||
let twitterConsumerSecret: string | null = ref(null);
|
||||
|
||||
async function init() {
|
||||
const meta = await os.api('admin/meta');
|
||||
uri = meta.uri;
|
||||
enableTwitterIntegration = meta.enableTwitterIntegration;
|
||||
twitterConsumerKey = meta.twitterConsumerKey;
|
||||
twitterConsumerSecret = meta.twitterConsumerSecret;
|
||||
uri.value = meta.uri;
|
||||
enableTwitterIntegration.value = meta.enableTwitterIntegration;
|
||||
twitterConsumerKey.value = meta.twitterConsumerKey;
|
||||
twitterConsumerSecret.value = meta.twitterConsumerSecret;
|
||||
}
|
||||
|
||||
function save() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
enableTwitterIntegration,
|
||||
twitterConsumerKey,
|
||||
twitterConsumerSecret,
|
||||
enableTwitterIntegration: enableTwitterIntegration.value,
|
||||
twitterConsumerKey: twitterConsumerKey.value,
|
||||
twitterConsumerSecret: twitterConsumerSecret.value,
|
||||
}).then(() => {
|
||||
fetchInstance();
|
||||
});
|
||||
|
@ -25,7 +25,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import XTwitter from './integrations.twitter.vue';
|
||||
import XGithub from './integrations.github.vue';
|
||||
import XDiscord from './integrations.discord.vue';
|
||||
@ -35,20 +35,20 @@ import * as os from '@/os';
|
||||
import { i18n } from '@/i18n';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
|
||||
let enableTwitterIntegration: boolean = $ref(false);
|
||||
let enableGithubIntegration: boolean = $ref(false);
|
||||
let enableDiscordIntegration: boolean = $ref(false);
|
||||
let enableTwitterIntegration: boolean = ref(false);
|
||||
let enableGithubIntegration: boolean = ref(false);
|
||||
let enableDiscordIntegration: boolean = ref(false);
|
||||
|
||||
async function init() {
|
||||
const meta = await os.api('admin/meta');
|
||||
enableTwitterIntegration = meta.enableTwitterIntegration;
|
||||
enableGithubIntegration = meta.enableGithubIntegration;
|
||||
enableDiscordIntegration = meta.enableDiscordIntegration;
|
||||
enableTwitterIntegration.value = meta.enableTwitterIntegration;
|
||||
enableGithubIntegration.value = meta.enableGithubIntegration;
|
||||
enableDiscordIntegration.value = meta.enableDiscordIntegration;
|
||||
}
|
||||
|
||||
const headerActions = $computed(() => []);
|
||||
const headerActions = computed(() => []);
|
||||
|
||||
const headerTabs = $computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePageMetadata({
|
||||
title: i18n.ts.integration,
|
||||
|
@ -69,7 +69,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import XHeader from './_header_.vue';
|
||||
import FormSwitch from '@/components/form/switch.vue';
|
||||
import FormInput from '@/components/form/input.vue';
|
||||
@ -81,65 +81,65 @@ import { fetchInstance } from '@/instance';
|
||||
import { i18n } from '@/i18n';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
|
||||
let useObjectStorage: boolean = $ref(false);
|
||||
let objectStorageBaseUrl: string | null = $ref(null);
|
||||
let objectStorageBucket: string | null = $ref(null);
|
||||
let objectStoragePrefix: string | null = $ref(null);
|
||||
let objectStorageEndpoint: string | null = $ref(null);
|
||||
let objectStorageRegion: string | null = $ref(null);
|
||||
let objectStoragePort: number | null = $ref(null);
|
||||
let objectStorageAccessKey: string | null = $ref(null);
|
||||
let objectStorageSecretKey: string | null = $ref(null);
|
||||
let objectStorageUseSSL: boolean = $ref(false);
|
||||
let objectStorageUseProxy: boolean = $ref(false);
|
||||
let objectStorageSetPublicRead: boolean = $ref(false);
|
||||
let objectStorageS3ForcePathStyle: boolean = $ref(true);
|
||||
let useObjectStorage: boolean = ref(false);
|
||||
let objectStorageBaseUrl: string | null = ref(null);
|
||||
let objectStorageBucket: string | null = ref(null);
|
||||
let objectStoragePrefix: string | null = ref(null);
|
||||
let objectStorageEndpoint: string | null = ref(null);
|
||||
let objectStorageRegion: string | null = ref(null);
|
||||
let objectStoragePort: number | null = ref(null);
|
||||
let objectStorageAccessKey: string | null = ref(null);
|
||||
let objectStorageSecretKey: string | null = ref(null);
|
||||
let objectStorageUseSSL: boolean = ref(false);
|
||||
let objectStorageUseProxy: boolean = ref(false);
|
||||
let objectStorageSetPublicRead: boolean = ref(false);
|
||||
let objectStorageS3ForcePathStyle: boolean = ref(true);
|
||||
|
||||
async function init() {
|
||||
const meta = await os.api('admin/meta');
|
||||
useObjectStorage = meta.useObjectStorage;
|
||||
objectStorageBaseUrl = meta.objectStorageBaseUrl;
|
||||
objectStorageBucket = meta.objectStorageBucket;
|
||||
objectStoragePrefix = meta.objectStoragePrefix;
|
||||
objectStorageEndpoint = meta.objectStorageEndpoint;
|
||||
objectStorageRegion = meta.objectStorageRegion;
|
||||
objectStoragePort = meta.objectStoragePort;
|
||||
objectStorageAccessKey = meta.objectStorageAccessKey;
|
||||
objectStorageSecretKey = meta.objectStorageSecretKey;
|
||||
objectStorageUseSSL = meta.objectStorageUseSSL;
|
||||
objectStorageUseProxy = meta.objectStorageUseProxy;
|
||||
objectStorageSetPublicRead = meta.objectStorageSetPublicRead;
|
||||
objectStorageS3ForcePathStyle = meta.objectStorageS3ForcePathStyle;
|
||||
useObjectStorage.value = meta.useObjectStorage;
|
||||
objectStorageBaseUrl.value = meta.objectStorageBaseUrl;
|
||||
objectStorageBucket.value = meta.objectStorageBucket;
|
||||
objectStoragePrefix.value = meta.objectStoragePrefix;
|
||||
objectStorageEndpoint.value = meta.objectStorageEndpoint;
|
||||
objectStorageRegion.value = meta.objectStorageRegion;
|
||||
objectStoragePort.value = meta.objectStoragePort;
|
||||
objectStorageAccessKey.value = meta.objectStorageAccessKey;
|
||||
objectStorageSecretKey.value = meta.objectStorageSecretKey;
|
||||
objectStorageUseSSL.value = meta.objectStorageUseSSL;
|
||||
objectStorageUseProxy.value = meta.objectStorageUseProxy;
|
||||
objectStorageSetPublicRead.value = meta.objectStorageSetPublicRead;
|
||||
objectStorageS3ForcePathStyle.value = meta.objectStorageS3ForcePathStyle;
|
||||
}
|
||||
|
||||
function save() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
useObjectStorage,
|
||||
objectStorageBaseUrl,
|
||||
objectStorageBucket,
|
||||
objectStoragePrefix,
|
||||
objectStorageEndpoint,
|
||||
objectStorageRegion,
|
||||
objectStoragePort,
|
||||
objectStorageAccessKey,
|
||||
objectStorageSecretKey,
|
||||
objectStorageUseSSL,
|
||||
objectStorageUseProxy,
|
||||
objectStorageSetPublicRead,
|
||||
objectStorageS3ForcePathStyle,
|
||||
useObjectStorage: useObjectStorage.value,
|
||||
objectStorageBaseUrl: objectStorageBaseUrl.value,
|
||||
objectStorageBucket: objectStorageBucket.value,
|
||||
objectStoragePrefix: objectStoragePrefix.value,
|
||||
objectStorageEndpoint: objectStorageEndpoint.value,
|
||||
objectStorageRegion: objectStorageRegion.value,
|
||||
objectStoragePort: objectStoragePort.value,
|
||||
objectStorageAccessKey: objectStorageAccessKey.value,
|
||||
objectStorageSecretKey: objectStorageSecretKey.value,
|
||||
objectStorageUseSSL: objectStorageUseSSL.value,
|
||||
objectStorageUseProxy: objectStorageUseProxy.value,
|
||||
objectStorageSetPublicRead: objectStorageSetPublicRead.value,
|
||||
objectStorageS3ForcePathStyle: objectStorageS3ForcePathStyle.value,
|
||||
}).then(() => {
|
||||
fetchInstance();
|
||||
});
|
||||
}
|
||||
|
||||
const headerActions = $computed(() => [{
|
||||
const headerActions = computed(() => [{
|
||||
asFullButton: true,
|
||||
icon: 'fas fa-check',
|
||||
text: i18n.ts.save,
|
||||
handler: save,
|
||||
}]);
|
||||
|
||||
const headerTabs = $computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePageMetadata({
|
||||
title: i18n.ts.objectStorage,
|
||||
|
@ -10,7 +10,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import XHeader from './_header_.vue';
|
||||
import FormSuspense from '@/components/form/suspense.vue';
|
||||
import * as os from '@/os';
|
||||
@ -28,14 +28,14 @@ function save() {
|
||||
});
|
||||
}
|
||||
|
||||
const headerActions = $computed(() => [{
|
||||
const headerActions = computed(() => [{
|
||||
asFullButton: true,
|
||||
icon: 'fas fa-check',
|
||||
text: i18n.ts.save,
|
||||
handler: save,
|
||||
}]);
|
||||
|
||||
const headerTabs = $computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePageMetadata({
|
||||
title: i18n.ts.other,
|
||||
|
@ -10,6 +10,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
import * as misskey from 'misskey-js';
|
||||
import MkMiniChart from '@/components/MkMiniChart.vue';
|
||||
import * as os from '@/os';
|
||||
@ -19,10 +21,10 @@ const props = defineProps<{
|
||||
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 => {
|
||||
chart = res;
|
||||
chart.value = res;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -139,7 +139,7 @@
|
||||
</template>
|
||||
|
||||
<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 {
|
||||
Chart,
|
||||
ArcElement,
|
||||
@ -196,20 +196,20 @@ Chart.register(
|
||||
//gradient,
|
||||
);
|
||||
|
||||
const rootEl = $ref<HTMLElement>();
|
||||
const chartEl = $ref<HTMLCanvasElement>(null);
|
||||
let stats: any = $ref(null);
|
||||
let serverInfo: any = $ref(null);
|
||||
let topSubInstancesForPie: any = $ref(null);
|
||||
let topPubInstancesForPie: any = $ref(null);
|
||||
let usersComparedToThePrevDay: any = $ref(null);
|
||||
let notesComparedToThePrevDay: any = $ref(null);
|
||||
let federationPubActive = $ref<number | null>(null);
|
||||
let federationPubActiveDiff = $ref<number | null>(null);
|
||||
let federationSubActive = $ref<number | null>(null);
|
||||
let federationSubActiveDiff = $ref<number | null>(null);
|
||||
let newUsers = $ref(null);
|
||||
let activeInstances = $shallowRef(null);
|
||||
const rootEl = ref<HTMLElement>();
|
||||
const chartEl = ref<HTMLCanvasElement>(null);
|
||||
let stats: any = ref(null);
|
||||
let serverInfo: any = ref(null);
|
||||
let topSubInstancesForPie: any = ref(null);
|
||||
let topPubInstancesForPie: any = ref(null);
|
||||
let usersComparedToThePrevDay: any = ref(null);
|
||||
let notesComparedToThePrevDay: any = ref(null);
|
||||
let federationPubActive = ref<number | null>(null);
|
||||
let federationPubActiveDiff = ref<number | null>(null);
|
||||
let federationSubActive = ref<number | null>(null);
|
||||
let federationSubActiveDiff = ref<number | null>(null);
|
||||
let newUsers = ref(null);
|
||||
let activeInstances = shallowRef(null);
|
||||
const queueStatsConnection = markRaw(stream.useChannel('queueStats'));
|
||||
const now = new Date();
|
||||
let chartInstance: Chart = null;
|
||||
@ -252,7 +252,7 @@ async function renderChart() {
|
||||
|
||||
const color = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent'));
|
||||
|
||||
chartInstance = new Chart(chartEl, {
|
||||
chartInstance = new Chart(chartEl.value, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
//labels: new Array(props.limit).fill(0).map((_, i) => getDate(i).toLocaleString()).slice().reverse(),
|
||||
@ -394,26 +394,26 @@ onMounted(async () => {
|
||||
renderChart();
|
||||
|
||||
os.api('stats', {}).then(statsResponse => {
|
||||
stats = statsResponse;
|
||||
stats.value = statsResponse;
|
||||
|
||||
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 => {
|
||||
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 => {
|
||||
federationPubActive = chart.pubActive[0];
|
||||
federationPubActiveDiff = chart.pubActive[0] - chart.pubActive[1];
|
||||
federationSubActive = chart.subActive[0];
|
||||
federationSubActiveDiff = chart.subActive[0] - chart.subActive[1];
|
||||
federationPubActive.value = chart.pubActive[0];
|
||||
federationPubActiveDiff.value = chart.pubActive[0] - chart.pubActive[1];
|
||||
federationSubActive.value = chart.subActive[0];
|
||||
federationSubActiveDiff.value = chart.subActive[0] - chart.subActive[1];
|
||||
});
|
||||
|
||||
os.apiGet('federation/stats', { limit: 10 }).then(res => {
|
||||
topSubInstancesForPie = res.topSubInstances.map(x => ({
|
||||
topSubInstancesForPie.value = res.topSubInstances.map(x => ({
|
||||
name: x.host,
|
||||
color: x.themeColor,
|
||||
value: x.followersCount,
|
||||
@ -421,7 +421,7 @@ onMounted(async () => {
|
||||
os.pageWindow(`/instance-info/${x.host}`);
|
||||
},
|
||||
})).concat([{ name: '(other)', color: '#80808080', value: res.otherFollowersCount }]);
|
||||
topPubInstancesForPie = res.topPubInstances.map(x => ({
|
||||
topPubInstancesForPie.value = res.topPubInstances.map(x => ({
|
||||
name: x.host,
|
||||
color: x.themeColor,
|
||||
value: x.followingCount,
|
||||
@ -432,21 +432,21 @@ onMounted(async () => {
|
||||
});
|
||||
|
||||
os.api('admin/server-info').then(serverInfoResponse => {
|
||||
serverInfo = serverInfoResponse;
|
||||
serverInfo.value = serverInfoResponse;
|
||||
});
|
||||
|
||||
os.api('admin/show-users', {
|
||||
limit: 5,
|
||||
sort: '+createdAt',
|
||||
}).then(res => {
|
||||
newUsers = res;
|
||||
newUsers.value = res;
|
||||
});
|
||||
|
||||
os.api('federation/instances', {
|
||||
sort: '+lastCommunicatedAt',
|
||||
limit: 25,
|
||||
}).then(res => {
|
||||
activeInstances = res;
|
||||
activeInstances.value = res;
|
||||
});
|
||||
|
||||
nextTick(() => {
|
||||
@ -461,9 +461,9 @@ onBeforeUnmount(() => {
|
||||
queueStatsConnection.dispose();
|
||||
});
|
||||
|
||||
const headerActions = $computed(() => []);
|
||||
const headerActions = computed(() => []);
|
||||
|
||||
const headerTabs = $computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePageMetadata({
|
||||
title: i18n.ts.dashboard,
|
||||
|
@ -14,7 +14,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||
import FormButton from '@/components/MkButton.vue';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
@ -24,36 +24,36 @@ import { fetchInstance } from '@/instance';
|
||||
import { i18n } from '@/i18n';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
|
||||
let proxyAccount: any = $ref(null);
|
||||
let proxyAccountId: any = $ref(null);
|
||||
let proxyAccount: any = ref(null);
|
||||
let proxyAccountId: any = ref(null);
|
||||
|
||||
async function init() {
|
||||
const meta = await os.api('admin/meta');
|
||||
proxyAccountId = meta.proxyAccountId;
|
||||
if (proxyAccountId) {
|
||||
proxyAccount = await os.api('users/show', { userId: proxyAccountId });
|
||||
proxyAccountId.value = meta.proxyAccountId;
|
||||
if (proxyAccountId.value) {
|
||||
proxyAccount.value = await os.api('users/show', { userId: proxyAccountId.value });
|
||||
}
|
||||
}
|
||||
|
||||
function chooseProxyAccount() {
|
||||
os.selectUser().then(user => {
|
||||
proxyAccount = user;
|
||||
proxyAccountId = user.id;
|
||||
proxyAccount.value = user;
|
||||
proxyAccountId.value = user.id;
|
||||
save();
|
||||
});
|
||||
}
|
||||
|
||||
function save() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
proxyAccountId: proxyAccountId,
|
||||
proxyAccountId: proxyAccountId.value,
|
||||
}).then(() => {
|
||||
fetchInstance();
|
||||
});
|
||||
}
|
||||
|
||||
const headerActions = $computed(() => []);
|
||||
const headerActions = computed(() => []);
|
||||
|
||||
const headerTabs = $computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePageMetadata({
|
||||
title: i18n.ts.proxyAccount,
|
||||
|
@ -53,10 +53,10 @@ const active = ref(0);
|
||||
const delayed = ref(0);
|
||||
const waiting = ref(0);
|
||||
const jobs = ref([]);
|
||||
let chartProcess = $ref<InstanceType<typeof XChart>>();
|
||||
let chartActive = $ref<InstanceType<typeof XChart>>();
|
||||
let chartDelayed = $ref<InstanceType<typeof XChart>>();
|
||||
let chartWaiting = $ref<InstanceType<typeof XChart>>();
|
||||
let chartProcess = ref<InstanceType<typeof XChart>>();
|
||||
let chartActive = ref<InstanceType<typeof XChart>>();
|
||||
let chartDelayed = ref<InstanceType<typeof XChart>>();
|
||||
let chartWaiting = ref<InstanceType<typeof XChart>>();
|
||||
|
||||
const props = defineProps<{
|
||||
domain: string;
|
||||
@ -68,10 +68,10 @@ const onStats = (stats) => {
|
||||
delayed.value = stats[props.domain].delayed;
|
||||
waiting.value = stats[props.domain].waiting;
|
||||
|
||||
chartProcess.pushData(stats[props.domain].activeSincePrevTick);
|
||||
chartActive.pushData(stats[props.domain].active);
|
||||
chartDelayed.pushData(stats[props.domain].delayed);
|
||||
chartWaiting.pushData(stats[props.domain].waiting);
|
||||
chartProcess.value.pushData(stats[props.domain].activeSincePrevTick);
|
||||
chartActive.value.pushData(stats[props.domain].active);
|
||||
chartDelayed.value.pushData(stats[props.domain].delayed);
|
||||
chartWaiting.value.pushData(stats[props.domain].waiting);
|
||||
};
|
||||
|
||||
const onStatsLog = (statsLog) => {
|
||||
@ -87,10 +87,10 @@ const onStatsLog = (statsLog) => {
|
||||
dataWaiting.push(stats[props.domain].waiting);
|
||||
}
|
||||
|
||||
chartProcess.setData(dataProcess);
|
||||
chartActive.setData(dataActive);
|
||||
chartDelayed.setData(dataDelayed);
|
||||
chartWaiting.setData(dataWaiting);
|
||||
chartProcess.value.setData(dataProcess);
|
||||
chartActive.value.setData(dataActive);
|
||||
chartDelayed.value.setData(dataDelayed);
|
||||
chartWaiting.value.setData(dataWaiting);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -9,7 +9,7 @@
|
||||
</template>
|
||||
|
||||
<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 XHeader from './_header_.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
@ -18,7 +18,7 @@ import * as config from '@/config';
|
||||
import { i18n } from '@/i18n';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
|
||||
let tab = $ref('deliver');
|
||||
let tab = ref('deliver');
|
||||
|
||||
function clear() {
|
||||
os.confirm({
|
||||
@ -32,7 +32,7 @@ function clear() {
|
||||
});
|
||||
}
|
||||
|
||||
const headerActions = $computed(() => [{
|
||||
const headerActions = computed(() => [{
|
||||
asFullButton: true,
|
||||
icon: 'fas fa-up-right-from-square',
|
||||
text: i18n.ts.dashboard,
|
||||
@ -41,7 +41,7 @@ const headerActions = $computed(() => [{
|
||||
},
|
||||
}]);
|
||||
|
||||
const headerTabs = $computed(() => [{
|
||||
const headerTabs = computed(() => [{
|
||||
key: 'deliver',
|
||||
title: 'Deliver',
|
||||
}, {
|
||||
|
@ -17,14 +17,14 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import XHeader from './_header_.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import * as os from '@/os';
|
||||
import { i18n } from '@/i18n';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
|
||||
let relays: any[] = $ref([]);
|
||||
let relays: any[] = ref([]);
|
||||
|
||||
async function addRelay() {
|
||||
const { canceled, result: inbox } = await os.inputText({
|
||||
@ -60,20 +60,20 @@ function remove(inbox: string) {
|
||||
|
||||
function refresh() {
|
||||
os.api('admin/relays/list').then((relayList: any) => {
|
||||
relays = relayList;
|
||||
relays.value = relayList;
|
||||
});
|
||||
}
|
||||
|
||||
refresh();
|
||||
|
||||
const headerActions = $computed(() => [{
|
||||
const headerActions = computed(() => [{
|
||||
asFullButton: true,
|
||||
icon: 'fas fa-plus',
|
||||
text: i18n.ts.addRelay,
|
||||
handler: addRelay,
|
||||
}]);
|
||||
|
||||
const headerTabs = $computed(() => []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePageMetadata({
|
||||
title: i18n.ts.relays,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user